han*_*aad 163 c++ templates sfinae enable-if c++11
Scott Meyers发表了他的下一本书EC++ 11的内容和状态.他写道,书中的一个项目可能是"避免std::enable_if功能签名".
std::enable_if 可以用作函数参数,返回类型或类模板或函数模板参数,以有条件地从重载解析中删除函数或类.
在这个问题中,显示了所有三个解决方案
作为功能参数:
template<typename T>
struct Check1
{
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, int>::value >::type* = 0) { return 42; }
template<typename U = T>
U read(typename std::enable_if<
std::is_same<U, double>::value >::type* = 0) { return 3.14; }
};
Run Code Online (Sandbox Code Playgroud)
作为模板参数:
template<typename T>
struct Check2
{
template<typename U = T, typename std::enable_if<
std::is_same<U, int>::value, int>::type = 0>
U read() { return 42; }
template<typename U = T, typename std::enable_if<
std::is_same<U, double>::value, int>::type = 0>
U read() { return 3.14; }
};
Run Code Online (Sandbox Code Playgroud)
作为返回类型:
template<typename T>
struct Check3
{
template<typename U = T>
typename std::enable_if<std::is_same<U, int>::value, U>::type read() {
return 42;
}
template<typename U = T>
typename std::enable_if<std::is_same<U, double>::value, U>::type read() {
return 3.14;
}
};
Run Code Online (Sandbox Code Playgroud)
std::enable_if在函数签名中"将使用视为返回类型(不是正常函数签名的一部分,而是模板特化的一部分)?R. *_*des 106
将hack放入模板参数中.
该enable_if模板参数方法比别人至少有两个好处:
可读性:enable_if use和return/argument类型不会合并为一个混乱的typename disambiguators和嵌套类型访问块; 即使可以使用别名模板来缓解消歧器和嵌套类型的混乱,但仍然会将两个不相关的事物合并在一起.enable_if use与模板参数有关,而与返回类型无关.将它们放在模板参数中意味着它们更接近重要的事物;
普遍适用性:构造函数没有返回类型,并且一些运算符不能有额外的参数,因此其他两个选项都不能应用于任何地方.将enable_if放在模板参数中无处不在,因为您只能在模板上使用SFINAE.
对我来说,可读性方面是这种选择的重要推动因素.
Tem*_*Rex 56
std::enable_if在模板参数推导期间依赖于" 替换失败不是错误 "(又名SFINAE)原则.这是一个非常脆弱的语言功能,你需要非常小心地做到正确.
enable_if包含嵌套模板或类型定义(提示:查找::标记),那么这些嵌套的模型或类型的解析通常是非推导的上下文.在这种未推断的上下文上的任何替换失败都是错误的.enable_if重载中的各种条件不能有任何重叠,因为重载分辨率会模糊不清.这是作为作者需要自己检查的内容,尽管您会收到良好的编译器警告.enable_if在重载决策期间操纵可行功能集,这可能具有令人惊讶的交互,这取决于从其他范围引入的其他功能的存在(例如通过ADL).这使它不是很强大.简而言之,当它工作时,它可以工作,但是当它不工作时,它可能很难调试.一个非常好的替代方法是使用标签分派,即委托给一个实现函数(通常在detail命名空间或帮助器类中),它接收基于你在中使用的相同编译时条件的伪参数enable_if.
template<typename T>
T fun(T arg)
{
return detail::fun(arg, typename some_template_trait<T>::type() );
}
namespace detail {
template<typename T>
fun(T arg, std::false_type /* dummy */) { }
template<typename T>
fun(T arg, std::true_type /* dummy */) {}
}
Run Code Online (Sandbox Code Playgroud)
标签分派不会操纵重载集,但可以通过编译时表达式(例如,在类型特征中)提供适当的参数来帮助您精确选择所需的函数.根据我的经验,这更容易调试和正确.如果你是一个有着复杂类型特征的有抱负的图书馆作家,你可能需要enable_if某种方式,但对于大多数经常使用的编译时条件,不建议这样做.
应该首选哪种解决方案,为什么我应该避免其他解决方案?
模板参数
它可能很容易被错误地使用,并产生带有过载的错误:
template<typename T, typename = std::enable_if_t<std::is_same<T, int>::value>>
void f() {/*...*/}
template<typename T, typename = std::enable_if_t<std::is_same<T, float>::value>>
void f() {/*...*/} // Redefinition: both are just template<typename, typename> f()
Run Code Online (Sandbox Code Playgroud)注意typename = std::enable_if_t<cond>而不是正确std::enable_if_t<cond, int>::type = 0
返回类型:
最后,在函数参数中:
+,-,*,...)void* = nullptr)(因此函数指针会有所不同,依此类推)成员函数模板和非成员函数模板有什么区别吗?
继承和之间有细微的差异using:
根据using-declarator(强调我的):
通过对using-declarator中的名称执行合格的名称查找([basic.lookup.qual],[class.member.lookup]),可以找到using-declarator引入的声明集,但不包括按说明隐藏的函数下面。
...
当using-declarator将基类的声明带入派生类时,派生类中的成员函数和成员函数模板将覆盖和/或隐藏具有相同名称,parameter-type-list,cv-的成员函数和成员函数模板。限定和ref限定符(如果有)在基类中(而不是冲突)。此类隐藏或覆盖的声明从using-declarator引入的声明集中排除。
因此,对于模板参数和返回类型,在以下情况下方法都是隐藏的:
struct Base
{
template <std::size_t I, std::enable_if_t<I == 0>* = nullptr>
void f() {}
template <std::size_t I>
std::enable_if_t<I == 0> g() {}
};
struct S : Base
{
using Base::f; // Useless, f<0> is still hidden
using Base::g; // Useless, g<0> is still hidden
template <std::size_t I, std::enable_if_t<I == 1>* = nullptr>
void f() {}
template <std::size_t I>
std::enable_if_t<I == 1> g() {}
};
Run Code Online (Sandbox Code Playgroud)
演示(gcc错误地找到基本函数)。
鉴于有论点,类似的情况适用:
struct Base
{
template <std::size_t I>
void h(std::enable_if_t<I == 0>* = nullptr) {}
};
struct S : Base
{
using Base::h; // Base::h<0> is visible
template <std::size_t I>
void h(std::enable_if_t<I == 1>* = nullptr) {}
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
35693 次 |
| 最近记录: |