Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can constructor template cause ambiguity in the c++17 parameter deduction of class template

Consider a simple example:

template <class T>
struct foo {
    template <template <class> class TT>
    foo(TT<T>&&) {}
    foo(foo<T>&&){}
    foo() {}
};

int main() {
    foo      f1(foo<int>{}); //case 1.
    foo<int> f2(foo<int>{}); //case 2.
}

Case 1. causes ambiguity in the template argument deduction of foo class in clang but not in gcc. I thought that template functions (here - constructor) have lower priority in overload resolution. Is it not the case here?

Error message:

prog.cc:10:14: error: ambiguous deduction for template arguments of 'foo'
    foo      f1(foo<int>{}); //case 1.
             ^
prog.cc:4:5: note: candidate function [with T = int, TT = foo]
    foo(TT<T>&&) {}
    ^
prog.cc:5:5: note: candidate function [with T = int]
    foo(foo<T>&&){}
    ^
1 error generated.

[clang demo] [gcc demo]

like image 580
W.F. Avatar asked Sep 05 '17 11:09

W.F.


People also ask

What is template argument deduction?

Template argument deduction is used when selecting user-defined conversion function template arguments. A is the type that is required as the result of the conversion. P is the return type of the conversion function template.

Can a template be a template parameter?

Templates can be template parameters. In this case, they are called template parameters. The container adaptors std::stack, std::queue, and std::priority_queue use per default a std::deque to hold their arguments, but you can use a different container.

What can the template parameter in C++ template definition be?

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

Which is a correct example of template parameters?

For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.


1 Answers

This is a Clang bug. The fact the candidate set is formed from c'tors should be immaterial, since after the candidate set is formed the best overload is chosen using the same rules for ordering implicit conversion sequences and template function ordering.

To quote [over.match.funcs]/1:

The subclauses of [over.match.funcs] describe the set of candidate functions and the argument list submitted to overload resolution in each of the seven contexts in which overload resolution is used. The source transformations and constructions defined in these subclauses are only for the purpose of describing the overload resolution process. An implementation is not required to use such transformations and constructions.

This clearly states that the overload resolution process is the same always. The only difference is how the candidate set is formed.

And as specified by [over.match.class.deduct]/1

A set of functions and function templates is formed comprising:

  • For each constructor of the primary class template designated by the template-name, if the template is defined, a function template with the following properties:

    • The template parameters are the template parameters of the class template followed by the template parameters (including default template arguments) of the constructor, if any.

    • The types of the function parameters are those of the constructor.

    • The return type is the class template specialization designated by the template-name and template arguments corresponding to the template parameters obtained from the class template.

Each c'tor will introduce a pseudo function into the candidate set. Like this:

template <class T>                           foo(foo<T>&&) -> foo<T> 
template <class T, template<class> class TT> foo(TT<T>&&) -> foo<T> 

To illustrate further, if this was a free function bar:

template <template <class> class TT, class T>
void bar(TT<T>&&) {}

template <class T>
void bar(foo<T>&&){}

Then template function ordering would place the first overload lower than the second.

like image 192
StoryTeller - Unslander Monica Avatar answered Oct 01 '22 08:10

StoryTeller - Unslander Monica