Suppose I have a template class
template <typename T> class foo;
template <typename... Args>
struct foo<std::tuple<Args...>> {
std::tuple<Args...> t;
foo(Args&&... args): t{std::forward<Args>(args)...} { }
};
I understand that in this case Args&&...
are rvalue references, and I could have just as well written std::move
instead of std::forward
.
I can also have a constructor with lvalue references, like so
foo(const Args&... args): t{args...} { }
The question is whether it's possible to get the same behavior as with forwarding references, but for definite types? The reason I want this is so I can used syntax like
foo bar({. . .}, {. . .}, . . ., {. . .});
This works if I define the foo(Args&&... args)
constructor, but doesn't allow for a mixed scenario, where I want to initialize some of the member tuple elements with brace-enclosed initializer lists and have others copied from preexisting object instances.
Sure; there is a fancy and simple way.
The fancy way I will detail below. First the simple way: take by value.
Really, just do this. Forward is used to do the right thing if
Args
contains a reference.Taking by value adds one move over perfect forwarding, but reduces the requirement for overloads exponentially.
This is the fancy way. We type erase construction:
This type erases construction by copy or move.
It isn't perfectly transparent, but it is as close as I can get.
Double
{{}}
is required instead of single. It is a user defined conversion, so another one will not implicitly be done. We could add a universal ctor:'which works better if we add a sfinae teat that
U&&
can be used to implicitly constructT
.It has a few advantages, but they are marginal over just taking by value. For example, in C++17 non-movable types can be perfect forward constructed in some cases.