Variadic Deduction Guide Not Taken by G++, Taken by Clang++ - Who Is Correct

Variadic deduction guide not taken by g++, taken by clang++ - who is correct?

This is gcc bug 80871. The issue is, we end up with this set of candidates for deduction:

template <class... Types, class... Args>
list<Types...> __f(Args... ); // constructor

template <class... Args>
list<Args...> __f(Args... ); // deduction-guide

Both are valid (Types... can deduce as empty in the first case), but the call here should be ambiguous - neither is more specialized than the other. Types... does not participate in ordering here (similar to the example in [temp.deduct.partial]/12). So the correct behavior is to proceed to the next tiebreaker, which favors deduction-guides. Hence, this should be a list<int, double, char>.

However, gcc's behavior is to favor the constructor, hence the static_assert triggers becuase Types... would indeed be empty in that situation.

Deduction guides and variadic class templates with variadic template constructors - mismatched argument pack lengths

To simplify your example further, it appears that GCC does not implement variadic template arguments in deduction guides:

https://wandbox.org/permlink/4YsacnW9wYcoceDH

I didn't see any explicit mention of variadic templates in the wording for deduction guides in the standard or on cppreference.com. I see no interpretation of the standard that disallows this. Therefore I think this is a bug.

Is it legal to use an unexpanded parameter pack as the type of a template template parameter's non-type template parameter?

It is not valid because:

a type parameter pack cannot be expanded in its own parameter clause.

As from [temp.param]/17:

If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack ([dcl.fct]), then the template-parameter is a template parameter pack. A template parameter pack that is a parameter-declaration whose type contains one or more unexpanded packs is a pack expansion. ... A template parameter pack that is a pack expansion shall not expand a template parameter pack declared in the same template-parameter-list.

So consider the following invalid example:

template<typename... Ts, Ts... vals> struct mytuple {}; //invalid

The above example is invalid because the template type parameter pack Ts cannot be expanded in its own parameter list.

For the same reason, your code example is invalid.

C++17 template deduction guide not used for empty parameter set?

This is a gcc bug (just filed 81486). When deducing success(), we synthesize an overload set which consists of:

// from the constructors
template <class T> success<T> foo(T&& ); // looks like a forwarding reference
// but is really just an rvalue reference
template <class T> success<T> foo(T const& );

// from the deduction guides
template <class T> success<T> foo(T ); // this one is a bit redundant
success<void> foo();

And determine the return type as if it were invoked as foo(), which certainly should give you a type of success<void>. That it doesn't is a bug.

What are template deduction guides and when should we use them?

Template deduction guides are patterns associated with a template class that tell the compiler how to translate a set of constructor arguments (and their types) into template parameters for the class.

The simplest example is that of std::vector and its constructor that takes an iterator pair.

template<typename Iterator>
void func(Iterator first, Iterator last)
{
vector v(first, last);
}

The compiler needs to figure out what vector<T>'s T type will be. We know what the answer is; T should be typename std::iterator_traits<Iterator>::value_type. But how do we tell the compiler without having to type vector<typename std::iterator_traits<Iterator>::value_type>?

You use a deduction guide:

template<typename Iterator> vector(Iterator b, Iterator e) -> 
vector<typename std::iterator_traits<Iterator>::value_type>;

This tells the compiler that, when you call a vector constructor matching that pattern, it will deduce the vector specialization using the code on the right of ->.

You need guides when the deduction of the type from the arguments is not based on the type of one of those arguments. Initializing a vector from an initializer_list explicitly uses the vector's T, so it doesn't need a guide.

The left side doesn't necessarily specify an actual constructor. The way it works is that, if you use template constructor deduction on a type, it matches the arguments you pass against all deduction guides (actual constructors of the primary template provide implicit guides). If there is a match, it uses that to determine which template arguments to provide to the type.

But once that deduction is done, once the compiler figures out the template parameters for the type, initialization for the object of that type proceeds as if none of that happened. That is, the deduction guide selected does not have to match the constructor selected.

This also means that you can use guides with aggregates and aggregate initialization:

template<typename T>
struct Thingy
{
T t;
};

Thingy(const char *) -> Thingy<std::string>;

Thingy thing{"A String"}; //thing.t is a `std::string`.

So deduction guides are only used to figure out the type being initialized. The actual process of initialization works exactly as it did before, once that determination has been made.

Deduction and parameter pack that is not the last template parameter: is this valid code?

From that mentioned paragraph:

If a template-parameter of a class template, variable template, or alias template has a default template-argument, each subsequent template-parameter shall either have a default template-argument supplied or be a template parameter pack. If a template-parameter of a primary class template, primary variable template, or alias template is a template parameter pack, it shall be the last template-parameter. A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list ([dcl.fct]) of the function template or has a default argument ([temp.deduct]). A template parameter of a deduction guide template ([temp.deduct.guide]) that does not have a default argument shall be deducible from the parameter-type-list of the deduction guide template. [ Example:

template<class T1 = int, class T2> class B;   // error

// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { } // error
template<class... T, class U> void g() { } // error

— end example ]

In this case, template<typename... A, typename R> can't be deduced from the parameter-type-list of function template because with:

void f(A&&..., R(*)(A...))

A&&... is greedy, and it will consume g as A&& not R(*)(A...)

Speaking pedantic, as in temp.deduct.call-1:

For a function parameter pack that occurs at the end of the parameter-declaration-list, deduction is performed for each remaining argument of the call, taking the type P of the declarator-id of the function parameter pack as the corresponding function template parameter type. Each deduction deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. When a function parameter pack appears in a non-deduced context ([temp.deduct.type]), the type of that parameter pack is never deduced.

and as in temp.deduct.type#9:

If P has a form that contains <T> or <i>, then each argument Pi of the respective template argument list of P is compared with the corresponding argument Ai of the corresponding template argument list of A. If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context. If Pi is a pack expansion, then the pattern of Pi is compared with each remaining argument in the template argument list of A.

And it can't be specified as you can see in the third example.

clang is right when reject it.



Related Topics



Leave a reply



Submit