[英]Template constructor vs. non-template constructor in class any
想象一下像boost::any
这样的东西:
class any {
public:
any();
any(const any &);
any(any &&);
template<typename ValueType> any(const ValueType &);
template<typename ValueType> any(ValueType &&);
是否会为任何可能的any
调用适当的(复制/移动)构造函数? 或者它必须与SFINAE一起编写,例如:
template<typename ValueType,
typename = typename std::enable_if<
!std::is_same<any, typename std::decay<ValueType>::type>::value
>::type>
any(const ValueType& value)
template<typename ValueType,
typename = typename std::enable_if<
!std::is_same<any, typename std::decay<ValueType>::type>::value
>::type>
any(ValueType&& value)
问题是:我是否需要保护模板化构造函数(从某个值构造any
值)或者我可以保留它,因为非模板(复制/移动)构造函数将始终匹配any
? 如果可能的话,那么volatile
修饰符或一些奇怪的std::move((const any&)it)
怎么样?
谢谢你说,描述搜索构造函数的答案最为贴切。
编辑:构建any
包含另一个any
将是一个问题,我definitelly想要避免(SFINAE确保它不会发生)。
使用C ++ 11并引入Universal Reference (以及具有此类参数的构造函数),重载决策的规则将选择模板化版本。
事实是,如果编译器可以在模板化和非模板化函数之间进行选择,那么它将与非模板一起使用。 但只有当它们同样好的时候才会这样做:
[over.match.best]
最佳可行功能[over.match.best]
[...]鉴于这些定义,如果对于所有参数i ,ICS i (F1) 不是比ICS i (F2) 更差的转换序列,则可行函数F1被定义为比另一个可行函数F2更好的函数,并且然后
- 对于某些参数j ,ICS j (F1)是比ICS j (F2) 更好的转换序列,或者, 如果不是 ,
[...]
- F1是非模板功能,F2是功能模板专业化,[...]
也就是说,有两个构造函数声明如下:
any(const any &);
template <typename ValueType>
any(const ValueType &);
编译器将选择非模板 化版本,因为实例化模板化版本会产生完全相同的声明 。
但是,随着构造函数采用Unviersal Reference ,情况会发生根本变化:
any(const any &);
template <typename ValueType>
any(ValueType &&);
在使用常规直接初始化语法复制实例的上下文中:
any a;
any b{a};
的评价类型a
是一个左值any &
而不 const
改性剂。 在生成用于重载解析的候选构造函数集之后,编译器最终得到以下签名:
any(const any &); // non-template
any(any &); // instantiated template
接着:
[over.match.funcs]
候选函数和参数列表[over.match.funcs]
- 在候选者是函数模板的每种情况下,使用模板参数推导(14.8.3,14.8.2)生成候选函数模板特化。 然后以通常的方式将这些候选人作为候选职能处理。 给定名称可以引用一个或多个函数模板,也可以引用一组重载的非模板函数。 在这种情况下,从每个功能模板生成的候选函数与该组非模板候选函数组合。
也就是说, 模板版本是更好的匹配 ,这是编译器选择的。
但是,如果有人:
const any a; // const!
any b{a};
那么这次从构造函数生成的构造函数签名采用Universal Reference将与copy-constructor的非模板版本相同,因此只有非模板版本被调用。
如果可能的话,那么volatile修饰符或一些奇怪的std :: move((const any&))怎么样?
完全相同的情况发生。 Universal Reference构造函数是一个更好的匹配。
也就是说, std::move((const any&)it)
计算为const any &&
type的表达式。
非模板移动构造函数的参数可以采用非const rvalue引用 (因此根本不匹配,因为它缺少const
修饰符)。
非模板复制构造函数的参数可以采用const左值引用 (很好,const rvalue可以通过const lvalue引用绑定,但不是完全匹配 )。
然后,采用通用引用的实例化模板再次是将被调用的更好匹配。
作为一般规则,如果模板和非模板函数在其他方面同样匹配,则在模板版本上选择非模板版本。 由于您的any
复制/移动构造函数都是非模板,因此对于rvalues或常量左值,它们优先于模板构造函数。
但是感谢rvalue引用模板的特殊规则, template<typename ValueType> any(ValueType &&);
的推导类型template<typename ValueType> any(ValueType &&);
将是any&
更好的匹配。 因此,在复制非const左值时,您将调用模板化构造函数。
因此,对于该模板化构造函数,您需要一个SFINAE规则,但对于采用左值引用const的模板化构造函数则不需要。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.