Forwarding reference behavior with specific types

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 write std::move as well instead of std::forward .

I can also have a constructor with lvalue references like

 foo(const Args&... args): t{args...} { } 

The question is, is it possible to get the same behavior as with link forwarding, but for certain types? The reason I want this is because I can use syntax like

 foo bar({. . .}, {. . .}, . . ., {. . .}); 

This works if I define the constructor foo(Args&&... args) , but do not allow a mixed scenario where I want to initialize some elements of member elements using lists of initializers enclosed in curly braces and copy others from pre-existing object instances.

+7
c ++ forwarding-reference c ++ 14
source share
1 answer

Of course; there is a fancy and easy way.

A fancy way, which I will describe below. First a simple way: take by value.

 template <typename... Args> struct foo<std::tuple<Args...>> { std::tuple<Args...> t; foo(Args... args): t{std::forward<Args>(args)...} { } }; 

Really, just do it. Forward is used to work correctly if Args contains a link.

Taking by value adds one step over perfect forwarding, but reduces the need for congestion exponentially.


This is a fancy way. We print the erase design:

 template<class T> struct make_it { using maker=T(*)(void*); maker f; void* args; // make from move make_it( T&& t ): f([](void* pvoid)->T{ return std::move(*static_cast<T*>(pvoid)); }), args(std::addressof(t)) {} // make from copy make_it( T const& t ): f([](void* pvoid)->T{ return *(T const*)(pvoid); }), args(std::addressof(t)) {} operator T()&&{return std::move(*this)();} T operator()()&&{ return f(args); } }; 

This type erases the design by copying or moving.

 template <typename... Args> struct foo<std::tuple<Args...>> { std::tuple<Args...> t; foo(make_it<Args>... args): t{std::move(args)()...} { } }; 

It is not completely transparent, but it is as close as possible.

Double {{}} is required instead of single. This is a custom conversion, so no other will be done. We could add a universal ctor: '

  // make from universal template<class U> make_it( U&& u ): f([](void* pvoid)->T{ return std::forward<U>(*(U*)(pvoid)); }), args(std::addressof(u)) {} 

which works better if we add sfinae teat that U&& can be used to implicitly build T

This has several advantages, but they are marginal, only taking on meaning. For example, in C ++ 17, immovable types can be ideally developed in some cases.

+2
source share

All Articles