想象一下写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
的{{1}}将是一个问题,我确定要避免这种情况(SFINAE确保不会发生这种情况)。
答案 0 :(得分:7)
使用C ++ 11和 Universal Reference (以及带有此类参数的构造函数)的引入,重载决策的规则将选择模板化版本。
事实是,如果编译器可以在模板化和非模板化函数之间进行选择,那么它将与非模板一起使用。但只有当它们同样好的时候才会这样做 :
§13.3.3最佳可行功能
[over.match.best]
- 醇>
[...]鉴于这些定义,可行函数F1被定义为比另一个可行函数更好的函数 F2如果对于所有参数 i ,则ICS i (F1)不是比ICS 转换序列> i (F2),然后
- 对于某些参数 j ,ICS j (F1)是更好的转换序列而不是ICS < sub> 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
然后:
§13.3.1候选函数和参数列表
[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&amp;))怎么样?
完全相同的情况发生。 通用参考构造函数更匹配。
即,std::move((const any&)it)
评估const any &&
类型的表达式。
非模板移动构造函数的参数可以采用非const rvalue引用(因此根本不匹配,因为它缺少const
修饰符)。
非模板复制构造函数的参数可以采用 const左值引用(很好,const rvalue可以通过const lvalue引用绑定,但不是完全匹配强>)。
然后,采用通用参考的实例化模板再次将是一个更好的匹配将被调用。
答案 1 :(得分:3)
作为一般规则,如果模板和非模板函数在其他方面同样匹配,则选择非模板版本而不是模板版本。由于any
复制/移动构造函数是非模板的,因此对于rvalues或常量左值,它们优先于模板构造函数。
然而由于rvalue参考模板的特殊规则,template<typename ValueType> any(ValueType &&);
的推断类型将是any&
,这是更好匹配。因此,在复制非常量左值时,您将调用模板化构造函数。
因此,对于该模板化构造函数,您需要一个SFINAE规则,但对于带有左值引用的模板化构造函数则不需要。