SFINAE 仅启用从派生到基础的转换运算符 class
SFINAE to enable cast operator only from derived to base class
我有一个 class 模板 CFoo<T>
。我想允许对 CFoo
的其他实例进行隐式转换,但仅适用于那些模板参数是 T
的基础 class 的实例。
我尝试使用 SFINAE,但我尝试过的任何编译器(VC 2012 或 gcc)都没有成功:
#include <type_traits>
template <class T> class CFoo {
public:
template <class Q> operator
// typename std::enable_if<std::is_base_of<Q, T>::value, CFoo<Q>&>::type // SHOULD WORK?
// typename std::enable_if<1, CFoo<Q>&>::type // SHOULD WORK?
CFoo<Q>& // compiles, but doesn't restrict on Q like I want
() const {
return *(CFoo<Q>*)this;
}
};
class A {};
class B : public A {};
int main(int argc, char* argv[])
{
CFoo<B> b;
CFoo<A>& a = b;
return 0;
}
为什么 SFINAE 中的任何一个被注释掉的尝试在这里都不起作用?在这两种情况下,我都收到 a
无效初始化的错误,就好像我的操作员没有被调用一样。
根据[temp.deduct.conv]:
Template argument deduction is done by comparing the return type of the conversion function template (call
it P) with the type that is required as the result of the conversion (call it A; see 8.5, 13.3.1.5, and 13.3.1.6
for the determination of that type) as described in 14.8.2.5.
在简单的情况下:
template <class Q>
operator CFoo<Q>& const;
这很简单,我们尝试根据 CFoo<A>&
推导 CFoo<Q>&
。该部分还有其他规则,但最终推导成功 Q == A
.
您的其他两次尝试都因同样的原因而失败。我会选择更简单的:
template <class Q>
operator typename std::enable_if<1, CFoo<Q>&>::type const;
在这里,我们试图推断 typename std::enable_if<1, CFoo<Q>&>::type
。这是一个非推导上下文(它是一个 nested-name-specifier 类型,使用 qualified-id 指定),所以推导失败。所以不会考虑这个转换函数,所以赋值失败,因为没有找到转换。
您需要 return 类型作为推导上下文,因此 SFINAE 必须转到此处:
template <class Q,
typename = std::enable_if_t<std::is_base_of<Q, T>::value>>
operator CFoo<Q>& const;
这样,我们就可以推导 (CFoo<Q>&
) - 并且推导可以成功(如果 Q
是 T
的基数):
CFoo<A>& a = b; // OK
CFoo<int>& i = b; // deduction failure on Q, so there's no viable conversion function
// so this is an error
就是说,虽然我对解决模板难题很着迷,但正如 指出的那样,这确实不是一个好的解决方案,因为:
return *(CFoo<Q>*)this;
只是做一个 reinterpret_cast
(和一个 const_cast
)所以它真的不可能做任何合理的事情,你几乎肯定会(除非 CFoo
是微不足道的)结束 undefined通过尝试访问其具有错误类型的成员的行为。
您可能想要添加转换 constructor 而不是转换 function:
template <typename Q,
typename = std::enable_if_t<std::is_base_of<T, Q>::value>>
CFoo(CFoo<Q> const& ) { }
这样,当你这样做时:
CFoo<A> a = b; // a is not a reference anymore
您正在构建一个必须有效的新对象。
我有一个 class 模板 CFoo<T>
。我想允许对 CFoo
的其他实例进行隐式转换,但仅适用于那些模板参数是 T
的基础 class 的实例。
我尝试使用 SFINAE,但我尝试过的任何编译器(VC 2012 或 gcc)都没有成功:
#include <type_traits>
template <class T> class CFoo {
public:
template <class Q> operator
// typename std::enable_if<std::is_base_of<Q, T>::value, CFoo<Q>&>::type // SHOULD WORK?
// typename std::enable_if<1, CFoo<Q>&>::type // SHOULD WORK?
CFoo<Q>& // compiles, but doesn't restrict on Q like I want
() const {
return *(CFoo<Q>*)this;
}
};
class A {};
class B : public A {};
int main(int argc, char* argv[])
{
CFoo<B> b;
CFoo<A>& a = b;
return 0;
}
为什么 SFINAE 中的任何一个被注释掉的尝试在这里都不起作用?在这两种情况下,我都收到 a
无效初始化的错误,就好像我的操作员没有被调用一样。
根据[temp.deduct.conv]:
Template argument deduction is done by comparing the return type of the conversion function template (call it P) with the type that is required as the result of the conversion (call it A; see 8.5, 13.3.1.5, and 13.3.1.6 for the determination of that type) as described in 14.8.2.5.
在简单的情况下:
template <class Q>
operator CFoo<Q>& const;
这很简单,我们尝试根据 CFoo<A>&
推导 CFoo<Q>&
。该部分还有其他规则,但最终推导成功 Q == A
.
您的其他两次尝试都因同样的原因而失败。我会选择更简单的:
template <class Q>
operator typename std::enable_if<1, CFoo<Q>&>::type const;
在这里,我们试图推断 typename std::enable_if<1, CFoo<Q>&>::type
。这是一个非推导上下文(它是一个 nested-name-specifier 类型,使用 qualified-id 指定),所以推导失败。所以不会考虑这个转换函数,所以赋值失败,因为没有找到转换。
您需要 return 类型作为推导上下文,因此 SFINAE 必须转到此处:
template <class Q,
typename = std::enable_if_t<std::is_base_of<Q, T>::value>>
operator CFoo<Q>& const;
这样,我们就可以推导 (CFoo<Q>&
) - 并且推导可以成功(如果 Q
是 T
的基数):
CFoo<A>& a = b; // OK
CFoo<int>& i = b; // deduction failure on Q, so there's no viable conversion function
// so this is an error
就是说,虽然我对解决模板难题很着迷,但正如
return *(CFoo<Q>*)this;
只是做一个 reinterpret_cast
(和一个 const_cast
)所以它真的不可能做任何合理的事情,你几乎肯定会(除非 CFoo
是微不足道的)结束 undefined通过尝试访问其具有错误类型的成员的行为。
您可能想要添加转换 constructor 而不是转换 function:
template <typename Q,
typename = std::enable_if_t<std::is_base_of<T, Q>::value>>
CFoo(CFoo<Q> const& ) { }
这样,当你这样做时:
CFoo<A> a = b; // a is not a reference anymore
您正在构建一个必须有效的新对象。