0

I'm trying to understand the motivation behind in_place_t in the std::optional<T> constructors a little better. I get the use case to disambiguate construction of null optional vs. type T with default constructor. However if there are one or more arguments specified to the std::optional constructor, can't it then be assumed that they can always be forwarded to construct T ?

#include <optional>

struct X
{
   X(int, int) {}
};

int main()
{
   std::optional<X> op{std::in_place, 1,2};
   //std::optional<X> op{1,2}; Does not compile, but why can't it be assumed that 1,2 always go to the constructor of X 
}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
user3882729
  • 1,339
  • 8
  • 11

1 Answers1

3

It comes down to another ambiguity. One example: if T can be constructed from optional<T>, what should happen when an optional<T> is constructed from an optional<T> object? Is the desired behavior copy construction of optional<T> or forwarding of the parameter to the T constructor?

This was addressed in N3527 (whose link I obtained from an answer to a related question.) The possibility of forwarding arguments was considered, but dismissed.

However, there are also certain problems with this [perfect forwarding] model. First, there are exceptions to perfect forwarding: the tag nullopt is not forwarded. Also, arguments of type optional<T> are not. This becomes visible if T has such constructor:

struct MyType
{
  MyType(optional<MyType>, int = 0);
  // ... 
};

Also, in general, it is impossible to perfect-forward initializer_list as the special deduction rules are involved.

The paper also identified a problem with default construction under perfect forwarding. (A default constructed optional<T> would contain a default-constructed T instead of not containing a value.) This part could be side-stepped – at the cost of some consistency – by requiring the in_place_t tag to construct an optional<T> that contains a default-constructed T. I view this as the lesser obstacle and the ambiguity issue as the major one.


I suppose you could press on and ask why not forward two or more arguments (when the first one is not the tag)? At some point consistency does become a desirable goal. Keep the interface simple. If a tag is needed for zero or one parameters to be forwarded to the constructor of T, then require it for any number of parameters. Fewer rules to remember results in fewer programming errors.

JaMiT
  • 14,422
  • 4
  • 15
  • 31
  • This is a good answer, but I just wanted to add that it's not _just_ about the ambiguity for `T` being constructible from `optional` (although this is a good example), but it's actually _really tough_ for the developer implementing templates to have `Args...` forwarded from a _constructor_. Forwarding arguments are often unambiguously better matches than other constructors if they would otherwise have CV-differences of reference-collapsing occur. It's tough to write code that can forward an entire constructor to another one; `in_place_t` solves a whole lot of them. – Human-Compiler Jul 05 '21 at 05:10
  • @Human-Compiler could you please elaborate on what aspect is difficult w.r.t. forwarding Args... from a constructor vs. any other function? – user3882729 Dec 07 '22 at 11:44
  • @JaMiT W.r.t. ```If a tag is needed for zero or one parameters to be forwarded to the constructor of T, then require it for any number of parameters.``` interestingly enough a tag is not needed if there is exactly one argument. – user3882729 Dec 07 '22 at 11:51