Quuxplusone
Quuxplusone

Reputation: 27145

Questions about class template argument deduction in C++17

I'm trying to make sense of P0091r3 (the "template argument deduction for class templates" paper that has been adopted into the current C++ draft standard, N4606).

I believe I understand how it works in the simplest possible case, where the template-name identifies a single template:

template<class T>
struct S {
    S(T);
    S(const std::vector<T>&);
};

int main()
{
    std::vector<int> v;
    auto s = S(v);
}

S identifies the primary template, so we create a fictitious overload set consisting of

template<class T> void Sctor(T);
template<class T> void Sctor(const std::vector<T>&);

and perform overload resolution on the fictitious call

Sctor(v)

to determine that in this case we want to call the fictitious Sctor(const std::vector<T>&) [with T=int]. Which means we end up calling S<int>::S(const std::vector<int>&) and everything works great.

What I don't understand is how this is supposed to work in the presence of partial specializations.

template<class T>
struct S {
    S(T);
};

template<class T>
struct S<std::list<T>> {
    S(const std::vector<T>&);
};

int main()
{
    std::vector<int> v;
    auto s = S(v);
}

What we intuitively want here is a call to S<std::list<int>>::S(const std::vector<int>&). Is that what we actually get, though? and where is this specified?

Basically I don't intuitively understand what P0091r3 means by "the class template designated by the template-name": does that mean the primary template, or does it include all partial specializations and explicit full specializations as well?

(I also don't understand how P0091r3's changes to §7.1.6.2p2 don't break code using injected-class-names such as

template<class T>
struct iterator {
    iterator& operator++(int) {
        iterator result = *this;  // injected-class-name or placeholder?
        //...
    }
};

but that's a different question altogether.)


Are class template deduction and explicit deduction guides supported in any extant version of Clang or GCC (possibly under an -f flag, like -fconcepts is)? If so, I could play around with some of these examples in real life and probably clear up half of my confusion.

Upvotes: 4

Views: 847

Answers (2)

ecatmur
ecatmur

Reputation: 157344

This is somewhat skated over by the proposal, but I think the intent is that only constructors of the primary class template are considered. Evidence for this is that the new [class.template.deduction] has:

  • For each constructor of the class template designated by the template-name, a function template with the following properties is a candidate: [...]

If we're talking about "the" class template, then this is the primary class template, particularly as class template partial specializations are not found by name lookup ([temp.class.spec]/6). This is also how the prototype implementation (see below) appears to behave.

Within the paper, class template partial specializations are contemplated in the section "Pros and cons of implicit deduction guides", but rather out of concern that constructors within the main class template could trigger a hard (non-SFINAE) error:

template<class T> struct X {
   using ty = T::type;
   static auto foo() { return typename T::type{} };
   X(ty); #1
   X(decltype(foo())); #2
   X(T);
};
template<class T>
struct X<T*> { 
   X(...);
};
X x{(int *)0};

Your plea for class template partial specialization constructors to be considered is on the face of it reasonable, but note that it could result in ambiguity:

template<class T> struct Y { Y(T*); };
template<class T> struct Y<T*> { Y(T*); };
Y y{(int*) 0};

It would probably be desirable for the implicitly generated deduction guides to be ranked (as a tie-breaker) by specialization of the class template.

If you want to try out a prototype implementation, the authors have published their branch of clang on github: https://github.com/faisalv/clang/tree/clang-ctor-deduction.


Discussion in the paper ("A note on injected class names") indicates that injected-class-names take priority over template names; wording is added to ensure this:

The template-name shall name a class template that is not an injected-class-name.

Upvotes: 2

Nicol Bolas
Nicol Bolas

Reputation: 473407

I would say that the wording of P0091 as it currently stands is under-specified in this regard. It does need to make it clear whether it is just the primary class template or whether it includes the constructors of all specializations.

That being said, I believe that the intent of P0091 is that partial specializations do not participate in argument deduction. The feature is to allow the compiler to decide what a class's template arguments are. However, what selects a partial specialization is what those template arguments actually are. The way to get the S<std::list<T>> specialization is to use a std::list in the template argument list of S.

If you want to cause a specific parameter to use a specific specialization, you should use a deduction guide. That is what they're for, after all.

Upvotes: 1

Related Questions