First: the error message is indeed as given. There is just quotes after the word "argument", which is strange on its own. But here's the problem I'm trying to solve. I'm writing a class that stores a reference of a (template) type internally and should also accept convertible types:
template<typename T>
class Ref {
public:
Ref();
Ref(std::nullptr_t);
explicit Ref(T *value);
Ref(Ref const& value);
template<typename T2, typename std::enable_if<std::is_convertible<T2*, T*>::value, T2>::type>
Ref(Ref<T2> const& value);
template<typename T2, typename std::enable_if<std::is_convertible<T2*, T*>::value, T2>::type>
Ref(Ref<T2> &&value);
private:
T *_value;
};
Now I have 2 classes A and B:
class A {
};
class B : public A {
};
and am trying to assign a Ref instance for B to a Ref variable for class A:
Ref<B> t;
Ref<A> t2(t);
This should actually compile, but I get the mentioned error (clang) for the last 2 constructors (those taking a convertible type), which should actually kick in for that assignment. What needs to be done to make template argument deduction work here?
You are using std::enable_if
wrongly, this should be1 2:
template<typename T2,
typename =
typename std::enable_if<std::is_convertible<T2*, T*>::value>::type>
Ref(Ref<T2> const& value);
Here, the second template argument is defaulted to something that would fail if T2*
is not convertible to T1*
, which is what you want:
std::is_convertible<T2*, T*>::value
is true
, then this is equivalent to:template<typename T2, typename = void> // Ok, well-formed
Ref(Ref<T2> const& value);
template<typename T2, typename = /* Something ill-formed */>
Ref(Ref<T2> const& value);
In your original code, when the std::enable_if
was successful, your template was equivalent to:
template<typename T2, typename T2> // Well-formed, but T2 cannot be deduced
Ref(Ref<T2> const& value);
Which was not what you wanted (the compiler was not able to deduce both T2
in this case).
1 You can replace the typename std::enable_if<>::type
with std::enable_if_t<>
if you have access to C++14.
2 This is one possibility, the other one being to use std::enable_if_t<..., int> = 0
(or something alike), but I prefer the version with typename = ...
. They are not strictly equivalent in the general case, but in this precise case it does not matter.