In C++17, it will be possible to instantiate objects without specifying the template types. Basically, this code would compile:
std::pair p(2, 4.5); // deduces to std::pair<int, double> p(2, 4.5);
std::tuple t(4, 3, 2.5); // same as auto t = std::make_tuple(4, 3, 2.5);
So, assuming this code below:
template<typename... Ts>
struct Foo
{
Foo(Ts&&... ts) :
ts{std::forward_as_tuple(ts...)}
{}
std::tuple<Ts...> ts;
};
int main()
{
auto f = [] { return 42; };
Foo foo{f, [] { return 84; }};
}
Should I use std::decay
in the tuple declaration like this?
std::tuple<std::decay_t<Ts>...> ts;
Because this is how I'd write a function to return an object based on the deduced template type:
template<typename T>
auto make_baz(T&& t) -> baz<std::decay_t<T>>;
And I can see this pattern in the Foo's constructor, where it's using forwarding references to correctly pass the values to the tuple. I'm not sure if the type deduction here behaves the same way.
In what situations is std::decay useful? It is used in the standard library e.g. when passing arguments to a thread. Those need to be stored, by value, so you cannot store e.g. arrays. Instead, a pointer is stored and so on.
std::forward has a single use-case: to cast a templated function parameter of type forwarding reference ( T&& ) to the value category ( lvalue or rvalue ) the caller used to pass it. This allows rvalue arguments to be passed on as rvalues , and lvalues to be passed on as lvalues .
There's no need to change the internals of your class to make it work with class template argument deduction; that's what deduction guides are for.
The best place to start is by writing a make_X
function; whether you provide one or not, deciding on the desired signature will let you know if you need to write an explicit deduction guide or can rely on the implicit deduction guides inferred from your constructors.
Indeed, a deduction guide, whether implicit or explicit, behaves the same as a make_X
function (up to copy constructor elision).
Your desired makeFoo
would have the following declaration:
template<typename... Ts>
auto makeFoo(Ts&&... ts) -> Foo<std::decay_t<Ts>...>;
Since this performs a transformation on the template arguments, you need to provide an explicit deduction guide; this is syntactically identical to the declaration of makeFoo
, just with the auto make
removed:
template<typename... Ts>
Foo(Ts&&... ts) -> Foo<std::decay_t<Ts>...>;
If you don't provide an explicit deduction guide, one will be generated from your constructor, without any type transformations other than those which occur during template argument deduction:
template<typename... Ts>
Foo(Ts&&... ts) -> Foo<Ts...>;
This isn't what you want, since it doesn't apply std::decay_t
. Modifying the internals of your class (adding std::decay_t
to ts
) would work, but it's unnecessary when an explicit deduction guide solves the problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With