想象一下写下这样的东西boost::any:
class any {
public:
any();
any(const any &);
any(any &&);
template<typename ValueType> any(const ValueType &);
template<typename ValueType> any(ValueType &&);
Run Code Online (Sandbox Code Playgroud)
是否可以调用适当的(复制/移动)构造函数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)
Run Code Online (Sandbox Code Playgroud)
问题是:我是否需要保护模板化构造函数(any从某个值构造)或者我可以保留它,因为非模板(复制/移动)构造函数将始终匹配any?怎么样volatile修改或者一些奇怪的std::move((const any&)it),如果这是可能的吗?
谢谢你说,描述搜索构造函数的答案最为贴切.
编辑:构建any包含另一个any将是一个问题,我definitelly想要避免(SFINAE确保它不会发生).
使用C++ 11并引入Universal Reference(以及具有此类参数的构造函数),重载决策的规则将选择模板化版本.
事实是,如果编译器可以在模板化和非模板化函数之间进行选择,那么它将与非模板一起使用.但只有当它们同样好的时候才会这样做:
§13.3.3最佳可行功能
[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 &);
Run Code Online (Sandbox Code Playgroud)
编译器将选择非模板化版本,因为实例化模板化版本会产生完全相同的声明.
但是,随着构造函数采用Unviersal Reference,情况会发生根本变化:
any(const any &);
template <typename ValueType>
any(ValueType &&);
Run Code Online (Sandbox Code Playgroud)
在使用常规直接初始化语法复制实例的上下文中:
any a;
any b{a};
Run Code Online (Sandbox Code Playgroud)
的评价类型a是一个左值any & 而不的const改性剂.在生成用于重载解析的候选构造函数集之后,编译器最终得到以下签名:
any(const any &); // non-template
any(any &); // instantiated template
Run Code Online (Sandbox Code Playgroud)
然后:
§13.3.1候选函数和参数列表
[over.match.funcs]
- 在候选者是函数模板的每种情况下,使用模板参数推导(14.8.3,14.8.2)生成候选函数模板特化.然后以通常的方式将这些候选人作为候选职能处理.给定名称可以引用一个或多个函数模板,也可以引用一组重载的非模板函数.在这种情况下,从每个功能模板生成的候选函数与该组非模板候选函数组合.
也就是说,模板版本是更好的匹配,这是编译器选择的.
但是,如果有人:
const any a; // const!
any b{a};
Run Code Online (Sandbox Code Playgroud)
那么这次从构造函数生成的构造函数签名采用Universal Reference将与copy-constructor的非模板版本相同,因此只有非模板版本被调用.
如果可能的话,那么volatile修饰符或一些奇怪的std :: move((const any&))怎么样?
完全相同的情况发生.该通用参考构造函数是一个更好的匹配.
也就是说,std::move((const any&)it)评估const any &&类型的表达.
非模板移动构造函数的参数可以采用非const rvalue引用(因此根本不匹配,因为它缺少const修饰符).
非模板复制构造函数的参数可以采用const左值引用(很好,const rvalue可以通过const lvalue引用绑定,但不是完全匹配).
然后,实例化的模板以通用参考是一次更好的匹配会被调用.