Template argument deduction from inherited type - c++

I want a setup like the following:
template <typename T> class a {};
class b : public a<int> {};
template <typename T>
void do_foo(std::unique_ptr<a<T>> foo)
{
// Do something with foo
}
int main()
{
do_foo(std::make_unique<b>());
}
This fails to compile with a note saying template argument deduction/substitution failed and mismatched types 'a<T>' and 'b'. It's pretty self-explanatory. I can help the compiler along by writing do_foo<int>(std::make_unique<b>());, but then I'm repeating myself by writing int twice.
Is there a way to get the compiler to deduce the template parameter in this case? And what would you call this behaviour? I tried searching for things like "template type deduction for inherited type", "polymorphic template deduction" etc.

Is there a way to get the compiler to deduce the template parameter in this case?
No. Not in C++14 (or even C++20).
And what would you call this behaviour?
Standard compliant. To be specific, this paragraph applies:
[temp.deduct.call]
4 In general, the deduction process attempts to find template
argument values that will make the deduced A identical to A (after
the type A is transformed as described above). However, there are
three cases that allow a difference:
If the original P is a reference type, the deduced A (i.e., the type referred to by the reference) can be more cv-qualified than the
transformed A.
The transformed A can be another pointer or pointer to member type that can be converted to the deduced A via a qualification
conversion ([conv.qual]).
If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A.
Likewise, if P is a pointer to a class of the form
simple-template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A.
This is an exhaustive list of cases where a template argument can be validly deduced from a function argument even if it doesn't match the pattern of the function parameter exactly. The first and second bullets deal with things like
template<class A1> void func(A1&){}
template<class A2> void func(A2*){}
int main() {
const int i = 1;
func(i); // A1 = const int
func(&i); // A2 = const int
}
The third bullet is the one that is closest to our case. A class derived from a template specialization can be used to deduce a template parameter pertaining to its base. Why doesn't it work in your case? Because the function template parameter is unique_ptr<a<T>> and the argument you call it with is unique_ptr<b>. The unique_ptr specializations are not themselves related by inheritance. So they don't match the bullet, and deduction fails.
But it doesn't mean that a wrapper like unique_ptr prevents template argument deduction entirely. For instance:
template <typename> struct A {};
struct B : A<int> {};
template<typename> struct wrapper{};
template<> struct wrapper<B> : wrapper<A<int>> {};
template<typename T>
void do_smth(wrapper<A<T>>) {}
int main() {
do_smth(wrapper<B>{});
}
In this case, wrapper<B> derives from wrapper<A<int>>. So the third bullet is applicable. And by the complex (and recursive) process of template argument deduction, it allows B to match A<T> and deduce T = int.
TL;DR: unique_ptr<T> specializations cannot replicate the behavior of raw pointers. They don't inherit from the specializations of unique_ptr over T's bases. Maybe if reflection ever comes to C++, we'll be able to meta-program a smart pointer that does behave that way.

As workaround, you might add overload:
template <typename T>
void do_foo_impl(a<T>* foo)
{
return do_foo(std::unique_ptr<a<T>>(foo));
}
template <typename T>
auto do_foo(std::unique_ptr<T> foo) -> decltype(do_foo_impl(foo.release()))
{
do_foo_impl(foo.release());
}
Demo

Related

Overloading operator+() in global scope, for a template class inside a template class [duplicate]

I've stumbled over "Why is the template argument deduction not working here?" recently and the answers can be summed up to "It's a nondeduced context".
Specifically, the first one says it's such a thing and then redirects to the standard for "details", while the second one quotes the standard, which is cryptic to say the least.
Can someone please explain to mere mortals, like myself, what a nondeduced context is, when does it occur, and why does it occur?
Deduction refers to the process of determining the type of a template parameter from a given argument. It applies to function templates, auto, and a few other cases (e.g. partial specialization). For example, consider:
template <typename T> void f(std::vector<T>);
Now if you say f(x), where you declared std::vector<int> x;, then T is deduced as int, and you get the specialization f<int>.
In order for deduction to work, the template parameter type that is to be deduced has to appear in a deducible context. In this example, the function parameter of f is such a deducible context. That is, an argument in the function call expression allows us to determine what the template parameter T should be in order for the call expression to be valid.
However, there are also non-deduced contexts, where no deduction is possible. The canonical example is "a template parameter that appears to the left of a :::
template <typename> struct Foo;
template <typename T> void g(typename Foo<T>::type);
In this function template, the T in the function parameter list is in a non-deduced context. Thus you cannot say g(x) and deduce T. The reason for this is that there is no "backwards correspondence" between arbitrary types and members Foo<T>::type. For example, you could have specializations:
template <> struct Foo<int> { using type = double; };
template <> struct Foo<char> { using type = double; };
template <> struct Foo<float> { using type = bool; };
template <> struct Foo<long> { int type = 10; };
template <> struct Foo<unsigned> { };
If you call g(double{}) there are two possible answers for T, and if you call g(int{}) there is no answer. In general, there is no relationship between class template parameters and class members, so you cannot perform any sensible argument deduction.
Occasionally it is useful to inhibit argument deduction explicitly. This is for example the case for std::forward. Another example is when you have conversions from Foo<U> to Foo<T>, say, or other conversions (think std::string and char const *). Now suppose you have a free function:
template <typename T> bool binary_function(Foo<T> lhs, Foo<T> rhs);
If you call binary_function(t, u), then the deduction may be ambiguous and thus fail. But it is reasonable to deduce only one argument and not deduce the other, thus permitting implicit conversions. Now an explicitly non-deduced context is needed, for example like this:
template <typename T>
struct type_identity {
using type = T;
};
template <typename T>
bool binary_function(Foo<T> lhs, typename type_identity<Foo<T>>::type rhs)
{
return binary_function(lhs, rhs);
}
(You may have experienced such deduction problems with something like std::min(1U, 2L).)

template deduction and implicit constructors

Is there a way to make template deduction work with (implicit) conversion? Like the following example:
template<typename T> struct A {};
template<typename T> struct B
{
B(A<T>); // implicit A->B conversion
};
template<typename... Ts> void fun(B<Ts>...);
int main()
{
A<int> a;
fun(B(a)); // works
fun(a); // does not work (deduction failure)
}
My thoughts:
If A is a subclass of B, everything works. That means that deduction can do implicit conversion using upcasting. So it seems weird that it can not do implicit conversion using a constructor.
Overloading fun for A and B is possible in principle, but for multiple parameters, there are just too many combinations
Adding a deduction guideline (template<typename T> B(A<T>)->B<T>;) does not change anything.
EDIT: some context:
In my actual code, A is a (large) container, and B is a lightweight non-owning view object. The situation is similar to the fact that std::vector<T> can not be implicity converted to std::span<T> during deduction of T, even though for any concrete T, such a conversion exists.
Template argument deduction does not consider any potential type conversions - mainly because deduction happens before any such conversions can happen.
Basically, when the compiler sees fun(a), it first gathers a set of foo functions that are eligible to service that call. If a foo function template is found, the compiler tries to generate a concrete function from it by substituting the template arguments with the types of arguments passed in the call statement. That's where the template argument deduction happens. No type conversion can happen here because there is no concrete type to convert to. In your example, B<T> is not a type to convert to because T is unknown and it cannot be discovered from an argument of type A<int>. It is also not known whether an arbitrary instance of B<T> will be constructible from A<int> because different specializations of B may have different sets of constructors. Hence the deduction fails in your case.
If the deduction succeeds, the concrete function (with the deduced argument types) is added to the candidate set.
When the set of candidates is gathered, then the best matching candidate is chosen. At this point argument conversions are considered. The conversions are possible since the candidates are no longer templates, and the target types for conversions are known.
What you could do to work around it is to allow the compiler to deduce the template as well and then construct B<T> explicitly:
template<typename... Ts> void fun_impl(B<Ts>...);
template<template<typename> class X, typename... Ts>
std::enable_if_t<(std::is_constructible_v<B<Ts>, X<Ts>> && ...)> fun(X<Ts>... args)
{
fun_impl(B<Ts>(args)...);
}
int main()
{
A<int> a;
fun(B(a)); // works, instantiates fun<B, int>
fun(a); // works, instantiates fun<A, int>
}
I think you can provide different overload of func then you come up with and solve your problem of multiple possible conversions.
template<typename T> struct A {};
template<typename T> struct C {};
template<typename T> struct B
{
B(A<T>) {}
B(C<T>) {}
};
template<typename... Ts>
void fun(B<Ts>...)
{
LOGFUN;
}
template<typename... Ts>
void fun(Ts...arg)
{
LOGFUN;
fun(B{arg}...);
}
int main()
{
A<int> a;
C<int> c;
fun(B(a));
fun(a);
fun(c, a, c);
}
https://godbolt.org/z/ne31z8Kfa

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.

C++ - Template parameter deduction failed from subclass [duplicate]

I've stumbled over "Why is the template argument deduction not working here?" recently and the answers can be summed up to "It's a nondeduced context".
Specifically, the first one says it's such a thing and then redirects to the standard for "details", while the second one quotes the standard, which is cryptic to say the least.
Can someone please explain to mere mortals, like myself, what a nondeduced context is, when does it occur, and why does it occur?
Deduction refers to the process of determining the type of a template parameter from a given argument. It applies to function templates, auto, and a few other cases (e.g. partial specialization). For example, consider:
template <typename T> void f(std::vector<T>);
Now if you say f(x), where you declared std::vector<int> x;, then T is deduced as int, and you get the specialization f<int>.
In order for deduction to work, the template parameter type that is to be deduced has to appear in a deducible context. In this example, the function parameter of f is such a deducible context. That is, an argument in the function call expression allows us to determine what the template parameter T should be in order for the call expression to be valid.
However, there are also non-deduced contexts, where no deduction is possible. The canonical example is "a template parameter that appears to the left of a :::
template <typename> struct Foo;
template <typename T> void g(typename Foo<T>::type);
In this function template, the T in the function parameter list is in a non-deduced context. Thus you cannot say g(x) and deduce T. The reason for this is that there is no "backwards correspondence" between arbitrary types and members Foo<T>::type. For example, you could have specializations:
template <> struct Foo<int> { using type = double; };
template <> struct Foo<char> { using type = double; };
template <> struct Foo<float> { using type = bool; };
template <> struct Foo<long> { int type = 10; };
template <> struct Foo<unsigned> { };
If you call g(double{}) there are two possible answers for T, and if you call g(int{}) there is no answer. In general, there is no relationship between class template parameters and class members, so you cannot perform any sensible argument deduction.
Occasionally it is useful to inhibit argument deduction explicitly. This is for example the case for std::forward. Another example is when you have conversions from Foo<U> to Foo<T>, say, or other conversions (think std::string and char const *). Now suppose you have a free function:
template <typename T> bool binary_function(Foo<T> lhs, Foo<T> rhs);
If you call binary_function(t, u), then the deduction may be ambiguous and thus fail. But it is reasonable to deduce only one argument and not deduce the other, thus permitting implicit conversions. Now an explicitly non-deduced context is needed, for example like this:
template <typename T>
struct type_identity {
using type = T;
};
template <typename T>
bool binary_function(Foo<T> lhs, typename type_identity<Foo<T>>::type rhs)
{
return binary_function(lhs, rhs);
}
(You may have experienced such deduction problems with something like std::min(1U, 2L).)

Partial template specialization type collapsing rules

Sorry for the lack of a better title.
While trying to implement my own version of std::move and understanding how easy it was, I'm still confused by how C++ treats partial template specializations. I know how they work, but there's a sort of rule that I found weird and I would like to know the reasoning behind it.
template <typename T>
struct BaseType {
using Type = T;
};
template <typename T>
struct BaseType<T *> {
using Type = T;
};
template <typename T>
struct BaseType<T &> {
using Type = T;
};
using int_ptr = int *;
using int_ref = int &;
// A and B are now both of type int
BaseType<int_ptr>::Type A = 5;
BaseType<int_ref>::Type B = 5;
If there wasn't no partial specializations of RemoveReference, T would always be T: if I gave a int & it would still be a int & throughout the whole template.
However, the partial specialized templates seem to collapse references and pointers: if I gave a int & or a int * and if those types match with the ones from the specialized template, T would just be int.
This feature is extremely awesome and useful, however I'm curious and I would like to know the official reasoning / rules behind this not so obvious quirk.
If your template pattern matches T& to int&, then T& is int&, which implies T is int.
The type T in the specialization only related to the T in the primary template by the fact it was used to pattern match the first argument.
It may confuse you less to replace T with X or U in the specializations. Reusing variable names can be confusing.
template <typename T>
struct RemoveReference {
using Type = T;
};
template <typename X>
struct RemoveReference<X &> {
using Type = X;
};
and X& matches T. If X& is T, and T ia int&, then X is int.
Why does the standard say this?
Suppose we look af a different template specialization:
template<class T>
struct Bob;
template<class E, class A>
struct Bob<std::vector<E,A>>{
// what should E and A be here?
};
Partial specializations act a lot like function templates: so much so, in fact, that overloading function templates is often mistaken for partial specialization of them (which is not allowed). Given
template<class T>
void value_assign(T *t) { *t=T(); }
then obviously T must be the version of the argument type without the (outermost) pointer status, because we need that type to compute the value to assign through the pointer. We of course don't typically write value_assign<int>(&i); to call a function of this type, because the arguments can be deduced.
In this case:
template<class T,class U>
void accept_pair(std::pair<T,U>);
note that the number of template parameters is greater than the number of types "supplied" as input (that is, than the number of parameter types used for deduction): complicated types can provide "more than one type's worth" of information.
All of this looks very different from class templates, where the types must be given explicitly (only sometimes true as of C++17) and they are used verbatim in the template (as you said).
But consider the partial specializations again:
template<class>
struct A; // undefined
template<class T>
struct A<T*> { /* ... */ }; // #1
template<class T,class U>
struct A<std::pair<T,U>> { /* ... */ }; // #2
These are completely isomorphic to the (unrelated) function templates value_assign and accept_pair respectively. We do have to write, for example, A<int*> to use #1; but this is simply analogous to calling value_assign(&i): in particular, the template arguments are still deduced, only this time from the explicitly-specified type int* rather than from the type of the expression &i. (Because even supplying explicit template arguments requires deduction, a partial specialization must support deducing its template arguments.)
#2 again illustrates the idea that the number of types is not conserved in this process: this should help break the false impression that "the template parameter" should continue to refer to "the type supplied". As such, partial specializations do not merely claim a (generally unbounded) set of template arguments: they interpret them.
Yet another similarity: the choice among multiple partial specializations of the same class template is exactly the same as that for discarding less-specific function templates when they are overloaded. (However, since overload resolution does not occur in the partial specialization case, this process must get rid of all but one candidate there.)