Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template deduction fails for polymorphic class with default template parameters

Consider this example:

#include <memory>

template<typename T>
class A {};

template<typename T1, typename T2>
class B: public A<T1> {};

template<typename T = int>
void foo(std::shared_ptr< A<T> > test)
{

}

int main()
{
    auto p = std::make_shared<B<int, int>>();
    foo<int>(p);    // Works
    foo<>(p);       // Does not work
    foo(p);         // Does not work
}

I'm trying to get this to compile without explicitly specifying the type T for foo, but it doesn't work. I'm not sure why as if I explicitly the type T, it works just fine, but if I it, it doesn't compile, even though I've told the compiler the what the type T should be if I don't explicitly specify it.

I get why the compiler can't deduce the type T, but why can't it use my default type T when I don't specify it? How do I get around this and what is the "proper" way of doing this?

like image 753
Athena Avatar asked Oct 28 '22 01:10

Athena


1 Answers

The problem isn't related to the default template parameter. Instead, the type of p (std::shared_ptr<B<int, int>>) can't be matched against the std::shared_ptr<T> argument of the template foo: During template argument deduction, no conversions are taken into account, and passing a reference to a derived class instance as a base class reference is indeed a conversion.

You can fix the issue by explicitly upcasting the pointer that std::shared_ptr manages.

std::shared_ptr<A<int>> p = std::make_shared<B<int, int>>();

Now, these calls will work out as expected:

foo<>(p); // Ok, argument matches the signature
foo(p);   // Same...

Note that it makes a big difference whether the template argument type appears in the parameter list of a function template (making deduction possible) or not. Consider these two templates,

template <class T = int> void foo(T&&)
{
    std::cout << __PRETTY_FUNCTION__ << "\n";
}

template <class T = int> void bar(double)
{
    std::cout << __PRETTY_FUNCTION__ << "\n";
}

Both have the default type int, but when they are instantiated like this

foo<>(0.0); // outputs: void foo(T&&) [with T = double]
bar<>(0.0); // outputs: void bar(double) [with T = int]

the first instantiation uses the function argument to deduce the template parameter (results in double), while the second doesn't deduce anything and defaults to int.

like image 160
lubgr Avatar answered Nov 15 '22 09:11

lubgr