Consider the following snippet:
template <class T>
struct remove_pointer
{
};
template <class T>
struct remove_pointer<T*>
{
typedef T type;
};
template <typename T>
T
clone(const T& v)
{
return v;
}
template <typename T, typename U = typename remove_pointer<T>::type>
T
clone(const U& v)
{
return new U(v);
}
int main()
{
auto foo = clone<double>(42.0);
return 0;
}
This code generates compilation errors:
In function 'int main()':
30:34: error: call of overloaded 'clone(double)' is ambiguous
30:34: note: candidates are:
14:1: note: T clone(const T&) [with T = double]
22:1: note: T clone(const U&) [with T = double; U = double]
Why is the compiler deriving T=double, U=double in line 22? I thought it only should pass if T is a pointer type.
Compiler deduced U=double and didn't use your default argument at all. I think that you can try to have two different overloads for your clone function based on this answer.
Your example might look like this:
#include <iostream>
#include <type_traits>
template <typename T>
T
clone(const T& v, typename std::enable_if<!std::is_pointer<T>::value >::type* = 0)
{
std::cout << "non ptr clone" << std::endl;
return v;
}
template <typename T>
T
clone(const T v, typename std::enable_if<std::is_pointer<T>::value >::type* = 0)
{
std::cout << "ptr clone" << std::endl;
using noptrT = typename std::remove_reference<decltype(*v)>::type;
return new noptrT(*v);
}
int main()
{
auto foo = clone<double>(42.0);
std::cout << foo << std::endl;
double* ptr = new double(69.69);
auto bar = clone<double*>(ptr);
std::cout << *bar << std::endl;
delete ptr;
delete bar;
return 0;
}
Note that this solution also allows you to pass a function pointer
void fun() {}
...
clone<void(*)()>(fun);
which will result in compilation error (new cannot be applied to function type).
Why is the compiler deriving T=double, U=double in line 22? I thought it only should pass if T is a pointer type.
Because template arguments are deduced based on passed non-template arguments. You passed a double literal into an argument of type const U&, so U was deduced to be double. The default of the template argument is not used if the argument is deduced.
And the problem is that both the single template argument, as well as the two template argument overloads can be called with the same argument list, so the compiler doesn't know which overload you intended to call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With