8

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.

Mário Feroldi
  • 3,463
  • 2
  • 24
  • 49

1 Answers1

4

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.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • What's the advantage of that, assuming that the type deduction will happen for the object declaration? – Mário Feroldi Aug 25 '16 at 15:38
  • Sorry, what do you mean by "that"? Do you mean the `decay_t`, or something else? – ecatmur Aug 25 '16 at 15:49
  • The deduction guide. In C++17, this would occur right in the Foo instantiation, so you don't need to specify any type. Sorry if my question is unclear: what I want is to know whether the type deduction for T in Foo will be a reference if I pass an lvalue to the constructor. I'll edit my question. – Mário Feroldi Aug 25 '16 at 15:54
  • 1
    @thlst updated. You definitely want an explicit deduction guide, since you're performing a transformation on the template arguments. You want `Foo` to be usable normally, right? – ecatmur Aug 25 '16 at 16:20
  • `Foo(Ts&&... ts) -> Foo...>;` is this a valid syntax in C++1z? Did you mean `Foo...> Foo(Ts&&... ts)` ? – Piotr Skotnicki Aug 26 '16 at 07:43
  • @PiotrSkotnicki it's the syntax for an explicit deduction guide: see [[temp.deduct.guide]/1](http://eel.is/c++draft/temp.deduct.guide#1). Your syntax would declare a function template with the same name as the class template, not a deduction guide. – ecatmur Aug 26 '16 at 10:57
  • oh, so it's for this `std::pair p(1, 'a')` feature ? – Piotr Skotnicki Aug 26 '16 at 11:22
  • @PiotrSkotnicki yes. – ecatmur Aug 26 '16 at 11:24