首先:错误消息确实如此给出。在“论证”这个词后面只有引号,这本身就很奇怪。但这是我试图解决的问题。我正在编写一个内部存储(模板)类型引用的类,并且还应该接受可转换类型:
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;
};
现在我有2个A和B类:
class A {
};
class B : public A {
};
我正在尝试将B的Ref实例分配给A类的Ref变量:
Ref<B> t;
Ref<A> t2(t);
这实际上应该编译,但我得到了最后2个构造函数(那些采用可转换类型)的上述错误(clang),这实际上应该为该赋值启动。需要做些什么来使模板参数推导在这里工作?
答案 0 :(得分:8)
你错误地使用std::enable_if
,这应该是 1 2 :
template<typename T2,
typename =
typename std::enable_if<std::is_convertible<T2*, T*>::value>::type>
Ref(Ref<T2> const& value);
此处,如果T2*
无法转换为T1*
,则第二个模板参数默认为失败,这是您想要的:
std::is_convertible<T2*, T*>::value
为true
,则相当于: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);
在原始代码中,std::enable_if
成功后,您的模板相当于:
template<typename T2, typename T2> // Well-formed, but T2 cannot be deduced
Ref(Ref<T2> const& value);
这不是你想要的(编译器在这种情况下无法推断出T2
)。
1 如果您有权访问C ++ 14,则可以将typename std::enable_if<>::type
替换为std::enable_if_t<>
。
2 这是一种可能性,另一种是使用std::enable_if_t<..., int> = 0
(或类似的东西),但我更喜欢typename = ...
的版本。它们在一般情况下并不完全相同,但在这种精确的情况下并不重要。
答案 1 :(得分:4)
@Holt's answer很好,并且正确地解释了如何使用std::enable_if
。
无论如何,在这种情况下,你根本不需要使用它
static_assert
就足够了,错误消息会更好:
template<typename U>
Ref(Ref<U> const& value) {
static_assert(std::is_convertible<U, T>::value, "!");
// whatever you want
}
template<typename U>
Ref(Ref<U> &&value) {
static_assert(std::is_convertible<U, T>::value, "!");
// whatever you want
}
现在,类似这样的内容(C
无法转换为A
):
Ref<A> t3(Ref<C>{});
会给你一个这样的错误:
错误:静态断言失败:! static_assert(std :: is_convertible :: value,&#34;!&#34;);
Sfinae表达式通常用于(让我说)启用或禁用选择。
如果您没有一套有效的替代方案可以从中选择正确的方案,通常最好使用static_assert
。
答案 2 :(得分:2)
要完成Holt的回答,请注意您可以将nullptr_t
的默认构造函数和构造函数合并到Ref(std::nullptr_t = nullptr):_value{nullptr} {}
。
Ref(Ref const& value);
相当于Ref(Ref<T2> const& value);
whith T2=T
。
您的代码段错过了operator=
。使用copy and swap idiom,您可以使用它来对T2
上的测试进行分解:
template<typename T>
class Ref {
public:
Ref(std::nullptr_t = nullptr):_value{nullptr} {}
explicit Ref(T *value);
template<typename T2, typename = typename std::enable_if<std::is_convertible<T2*, T*>::value, T2>::type>
Ref(Ref<T2> const& value);
template<typename T2>
Ref(Ref<T2> &&value):Ref{}
{
swap(*this, value);
}
template<typename T2>
Ref& operator=(Ref<T2> value)
{
swap(*this, value);
return (*this);
}
template<typename T1, typename T2, typename = typename std::enable_if<std::is_convertible<T2*, T1*>::value>::type>
friend void swap(Ref<T1>& t1, Ref<T2>& t2)
{
using std::swap;
swap(t1._value, t2._value);
}
private:
T *_value;
};