Class template argument deduction failed with derived class - c++

#include <utility>
template<class T1, class T2>
struct mypair : std::pair<T1, T2>
{ using std::pair<T1, T2>::pair; };
int main()
{
(void)std::pair(2, 3); // It works
(void)mypair(2, 3); // It doesn't work
}
Is the above well formed?
Is it possible deduce the class template arguments in the second case if the constructors are being inherited? Are the constructors of std::pair participating in the creation of implicit deduction guides for mypair?
My compiler is g++ 7.2.0.

The short story: there is no rule in the standard that says how this would work, nor any rule that says that it doesn't work. So GCC and Clang conservatively reject rather than inventing a (non-standard) rule.
The long story: mypair's pair base class is a dependent type, so lookup of its constructors cannot succeed. For each specialization of mytype<T1, T2>, the corresponding constructors of pair<T1, T2> are constructors of mytype, but this is not a rule that can be meaningfully applied to a template prior to instantiation in general.
In principle, there could be a rule that says that you look at the constructors of the primary pair template in this situation (much as we do when looking up constructors of mypair itself for class template argument deduction), but no such rule actually exists in the standard currently. Such a rule quickly falls down, though, when the base class becomes more complex:
template<typename T> struct my_pair2 : std::pair<T, T> {
using pair::pair;
};
What constructors should be notionally injected here? And in cases like this, I think it's reasonably clear that this lookup cannot possibly work:
template<typename T> struct my_pair3 : arbitrary_metafunction<T>::type {
using arbitrary_metafunction<T>::type::type;
};
It's possible we'll get a rule change to allow deduction through your my_pair and the my_pair2 above if/when we get class template argument deduction rules for alias templates:
template<typename T> using my_pair3 = std::pair<T, T>;
my_pair3 mp3 = {1, 2};
The complexities involved here are largely the same as in the inherited constructor case. Faisal Vali (one of the other designers of class template argument deduction) has a concrete plan for how to make such cases work, but the C++ committee hasn't discussed this extension yet.

See Richard Smith's answer.
A previous version of this answer had stated that the following should work
template <class T> struct B { B(T ) { } };
template <class T> struct D : B<T> { using B<T>::B; };
B b = 4; // okay, obviously
D d = 4; // expected: okay
But this isn't really viable, and wouldn't even be a good idea to work as I thought it would (we inherit the constructors but not the deduction guides?)

Related

Can we overload template classes in C++, similar to how we overload functions? [duplicate]

Reading this question made me wonder: is there a technical reason for disallowing class templates overloads?
By overloading, I mean having several templates with the same names, but different parameters, for instance
template <typename T>
struct Foo {};
template <typename T1, typename T2>
struct Foo {};
template <unsigned int N>
struct Foo {};
The compiler manages to handle overloaded functions and function templates, wouldn't it be possible to apply the same techniques (e.g. name mangling) to class templates?
At first, I thought that perhaps that would cause some ambiguity issues when taking the template identifier alone, but the only time this can happen is when passing it as a template template argument, so the type of the parameter could be used to choose the appropriate overload:
template <template <typename> class T>
void A {};
template <template <unsigned int> class T>
void B {};
A<Foo> a; // resolves to Foo<T>
B<Foo> b; // resolves to Foo<N>
Do you think such feature could be useful? Is there some "good" (i.e. technical) reasons why this is not possible in current C++?
Section 12.5 from Templates the Complete Guide (Amazon) contains this quote:
You may legitimately wonder why only class templates can be partially specialized. The reasons are mostly historical.
It is probably possible to define the same mechanism for function templates (see Chapter 13).
In some ways the
effect of overloading function templates is similar, but there are also some subtle differences. These differences are
mostly related to the fact that the primary template needs to be
looked up when a use is encountered. The specializations are
considered only afterward, to determine which implementation should be
used.
In contrast, all overloaded function templates must be brought
into an overload set by looking them up, and they may come from
different namespaces or classes. This increases the likelihood of
unintentionally overloading a template name somewhat.
Conversely, it
is also imaginable to allow a form of overloading of class templates.
Here is an example:
// invalid overloading of class templates
template<typename T1, typename T2> class Pair;
template<int N1, int N2> class Pair;
However, there doesn't seem to be a pressing need for
such a mechanism.
Furthermore, the Design and Evolution of C++ (Amazon) contains this quote in section 15.10.3
I therefore concluded that we needed a mechanism for "specializing"
templates. This could be done either by accepting general overloading
or by some more specific mechanism. I chose a specific mechanism
because I thought I was primarily addressing irregularities caused by
irregularities in C and because suggestions of overloading invariably
creates a howl of protests. I was trying to be cautious and
conservative; I now consider that a mistake. Specialization as
originally defined was a restricted and anomalous form of overloading
that fitted poorly with the rest of the language.
Bold emphasis mine. I interpret this as saying that function overload resolution is more difficult to implement (and get right by users) than class specialization. So probably no real technical obstacles (similary for function template partial specialization) but an historical accident.
You cannot "overload" type parameter, non-type argument and template template parameter, but you can specialize variadic template:
template <typename... T>
struct Foo;
template <typename T1>
struct Foo<T1> {};
template <typename T1, typename T2>
struct Foo<T1,T2> {};
This has been around for a while now, but I still found this post when searching. Thanks to #log0 for providing me with a good start. Here is a solution that avoids needing to provide a template specialisation for all possible enumerations. It does make one assumption: that you can define each template expansion in terms of itself and its base classes. (This would be done in FooImpl below):
template <typename... T>
struct Foo;
template<typename T>
struct Foo<T> { /* implementation of base class goes here*/};
template <typename C, typename Base>
struct FooImpl : public Base { /* implementation of derived class goes here */};
template<typename C, typename... Bases>
struct Foo<C, Bases...> : FooImpl<C, Foo<Bases...> > { /*NO IMPLEMENTATION HERE */};
The use of FooImpl breaks the ambiguous recursion that otherwise results. This then allows declarations such as the following:
Foo<int> foo_int;
Foo<int, double> foo_int_double;
Foo<int, float, double> foo_int_float_double;
Perhaps this is how the STL now does it?

Why cant the c++ template compiler infer types from covariant smart pointer arguments?

I have the following code
https://godbolt.org/z/7d1arK
#include <memory>
template <typename T>
class A {
};
template <typename T>
class B : public A<T> {};
template <typename T>
void Foo(std::shared_ptr<A<T>> arg){}
int main(){
auto bptr = std::make_shared<B<int>>();
std::shared_ptr<A<int>> aptr = bptr;
// This compiles
Foo(aptr);
// This does not compile
Foo(bptr);
// This compiles
Foo<int>(bptr);
}
My question is why can't the compiler handle the line?
Foo(bptr);
Template argument deduction and user-defined conversions do not mix. Because they are related by a user-defined conversion, shared_ptr<A<int>> and shared_ptr<B<int>> are not "covariant" in the same sense as types like A<int>* and B<int>* for purposes of the C++ language.
Though that's enough to make the call illegal, this case is even harder because there's not a simple user-defined converting constructor or conversion function involved here, but a constructor template:
template <class T> class std::shared_ptr {
public:
template <class Y>
shared_ptr(const shared_ptr<Y>&);
// ...
};
So here the compiler would need to deduce both T and Y to determine T. "Obviously" Y will deduce to int, but the general case of this complication is not practical to solve.
And actually in this case, int is not the only possible value of T. The call Foo<const int>(bptr) would also be legal.
The C++ language specifies exactly when template argument deduction will and won't succeed, and with what resulting template arguments, so that we can be certain code working on one conforming compiler won't fail on another, at least not for this reason. At some point the line must be drawn, and one such line is "user-defined conversions are not considered when deducing template arguments".
As #SamVarshavchik and #aschepler mentioned, template deduction takes place without considering user-defined conversions, which would be necessary to convert from std::shared_ptr<B<int>> to std::shared_ptr<A<int>>. Therefore no suitable instantiation of the template is found and the call fails.
One way to enable this would be to use a wider range of templates, with a static assertion to narrow it down to enabling only actual derived classes of A:
#include <memory>
#include <type_traits>
template <typename T>
class A { };
template <typename T>
class B : public A<T> { };
class C { };
template <template<class> class Derived, class T>
void Foo(std::shared_ptr<Derived<T>> arg) {
static_assert(std::is_base_of_v<A<T>, Derived<T>>);
}
int main() {
auto bptr = std::make_shared<B<int>>();
std::shared_ptr<A<int>> aptr = bptr;
auto cptr = std::make_shared<C>();
// This compiles
Foo(aptr);
// This compiles
Foo(bptr);
// This does not compile
Foo(cptr);
}
In this case, Foo will be able to accept as an argument a std::shared_ptr to anything that inherits from A.
Like #SamVarshavchik said, user-defined implicit conversions are not allowed in template argument deduction. bptr is of type std::shared_ptr<B<int> > which is implicitly convertible to std::shared_ptr<A<int> > since B<int>* is implicitly convertible to A<int>*, but the template expects something like std::shared_ptr<A<T> > directly.

How do `shared_ptr`s achieve covariance?

It is possible to copy or construct a shared_ptr<Base> from shared_ptr<Deriver> (i.e. shared_ptr<Base> ptr = make_shared<Derived>()). But as we all know, template classes are not convertible to each other, even if the template arguments are. So how can shared_ptrs check if the value of their pointers are convertible and do the conversion if they are?
Yes, specializations of the same class template by default have almost no relationship and are essentially treated like unrelated types. But you can always define implicit conversions between class types by defining converting constructors (To::To(const From&)) and/or conversion functions (From::operator To() const).
So what std::shared_ptr does is define template converting constructors:
namespace std {
template <class T>
class shared_ptr {
public:
template <class Y>
shared_ptr(const shared_ptr<Y>&);
template <class Y>
shared_ptr(shared_ptr<Y>&&);
// ...
};
}
Though the declaration as shown would allow conversions from any shared_ptr to any other, not just when the template argument types are compatible. But the Standard also says about these constructors ([util.smartptr]/5 and [util.smartptr.const]/18 and util.smartptr.const]/21):
For the purposes of subclause [util.smartptr], a pointer type Y* is said to be compatible with a pointer type T* when either Y* is convertible to T* or Y is U[N] and T is cv U[].
The [...] constructor shall not participate in overload resolution unless Y* is compatible with T*.
Although this restriction could be done in any way, including compiler-specific features, most implementations will enforce the restriction using an SFINAE technique (Substitution Failure Is Not An Error). One possible implementation:
#include <cstddef>
#include <type_traits>
namespace std {
template <class Y, class T>
struct __smartptr_compatible
: is_convertible<Y*, T*> {};
template <class U, class V, size_t N>
struct __smartptr_compatible<U[N], V[]>
: bool_constant<is_same_v<remove_cv_t<U>, remove_cv_t<V>> &&
is_convertible_v<U*, V*>> {};
template <class T>
class shared_ptr {
public:
template <class Y, class = enable_if_t<__smartptr_compatible<Y, T>::value>>
shared_ptr(const shared_ptr<Y>&);
template <class Y, class = enable_if_t<__smartptr_compatible<Y, T>::value>>
shared_ptr(shared_ptr<Y>&&);
// ...
};
}
Here the helper template __smartptr_compatible<Y, T> acts as a "trait": it has a static constexpr member value which is true when the types are compatible as defined, or false otherwise. Then std::enable_if is a trait which has a member type called type when its first template argument is true, or does not have a member named type when its first template argument is false, making the type alias std::enable_if_t invalid.
So if template type deduction for either constructor deduces the type Y so that Y* is not compatible with T*, substituting that Y into the enable_if_t default template argument is invalid. Since that happens while substituting a deduced template argument, the effect is just to remove the entire function template from consideration for overload resolution. Sometimes an SFINAE technique is used to force selecting a different overload instead, or as here (most of the time), it can just make the user's code fail to compile. Though in the case of the compile error, it will help that a message appears somewhere in the output that the template was invalid, rather than some error even deeper within internal template code. (Also, an SFINAE setup like this makes it possible for a different template to use its own SFINAE technique to test whether or not a certain template specialization, type-dependent expression, etc. is or isn't valid.)
It works because shared_ptr has (among others) a templated constructor
template<typename U> shared_ptr(U * ptr);
If U* is not convertible to the shared_ptr's contained type then you will get an error buried somewhere in the shared_ptr implementation.

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.
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.
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.

Why is it not possible to overload class templates?

Reading this question made me wonder: is there a technical reason for disallowing class templates overloads?
By overloading, I mean having several templates with the same names, but different parameters, for instance
template <typename T>
struct Foo {};
template <typename T1, typename T2>
struct Foo {};
template <unsigned int N>
struct Foo {};
The compiler manages to handle overloaded functions and function templates, wouldn't it be possible to apply the same techniques (e.g. name mangling) to class templates?
At first, I thought that perhaps that would cause some ambiguity issues when taking the template identifier alone, but the only time this can happen is when passing it as a template template argument, so the type of the parameter could be used to choose the appropriate overload:
template <template <typename> class T>
void A {};
template <template <unsigned int> class T>
void B {};
A<Foo> a; // resolves to Foo<T>
B<Foo> b; // resolves to Foo<N>
Do you think such feature could be useful? Is there some "good" (i.e. technical) reasons why this is not possible in current C++?
Section 12.5 from Templates the Complete Guide (Amazon) contains this quote:
You may legitimately wonder why only class templates can be partially specialized. The reasons are mostly historical.
It is probably possible to define the same mechanism for function templates (see Chapter 13).
In some ways the
effect of overloading function templates is similar, but there are also some subtle differences. These differences are
mostly related to the fact that the primary template needs to be
looked up when a use is encountered. The specializations are
considered only afterward, to determine which implementation should be
used.
In contrast, all overloaded function templates must be brought
into an overload set by looking them up, and they may come from
different namespaces or classes. This increases the likelihood of
unintentionally overloading a template name somewhat.
Conversely, it
is also imaginable to allow a form of overloading of class templates.
Here is an example:
// invalid overloading of class templates
template<typename T1, typename T2> class Pair;
template<int N1, int N2> class Pair;
However, there doesn't seem to be a pressing need for
such a mechanism.
Furthermore, the Design and Evolution of C++ (Amazon) contains this quote in section 15.10.3
I therefore concluded that we needed a mechanism for "specializing"
templates. This could be done either by accepting general overloading
or by some more specific mechanism. I chose a specific mechanism
because I thought I was primarily addressing irregularities caused by
irregularities in C and because suggestions of overloading invariably
creates a howl of protests. I was trying to be cautious and
conservative; I now consider that a mistake. Specialization as
originally defined was a restricted and anomalous form of overloading
that fitted poorly with the rest of the language.
Bold emphasis mine. I interpret this as saying that function overload resolution is more difficult to implement (and get right by users) than class specialization. So probably no real technical obstacles (similary for function template partial specialization) but an historical accident.
You cannot "overload" type parameter, non-type argument and template template parameter, but you can specialize variadic template:
template <typename... T>
struct Foo;
template <typename T1>
struct Foo<T1> {};
template <typename T1, typename T2>
struct Foo<T1,T2> {};
This has been around for a while now, but I still found this post when searching. Thanks to #log0 for providing me with a good start. Here is a solution that avoids needing to provide a template specialisation for all possible enumerations. It does make one assumption: that you can define each template expansion in terms of itself and its base classes. (This would be done in FooImpl below):
template <typename... T>
struct Foo;
template<typename T>
struct Foo<T> { /* implementation of base class goes here*/};
template <typename C, typename Base>
struct FooImpl : public Base { /* implementation of derived class goes here */};
template<typename C, typename... Bases>
struct Foo<C, Bases...> : FooImpl<C, Foo<Bases...> > { /*NO IMPLEMENTATION HERE */};
The use of FooImpl breaks the ambiguous recursion that otherwise results. This then allows declarations such as the following:
Foo<int> foo_int;
Foo<int, double> foo_int_double;
Foo<int, float, double> foo_int_float_double;
Perhaps this is how the STL now does it?