c++ - "lazy man' s enable_if"是合法的 C++ 吗?

标签 c++ c++11 visual-c++ language-lawyer sfinae

我经常使用一种我称之为“懒人的 enable_if”的技术,我使用 decltype 和逗号运算符来启用基于某些模板输入的功能。这是一个小例子:

template <typename F>
auto foo(F&& f) -> decltype(f(0), void())
{
    std::cout << "1" << std::endl;
}

template <typename F>
auto foo(F&& f) -> decltype(f(0, 1), void())
{
    std::cout << "2" << std::endl;
}

使用 --std=c++11,g++ 4.7+ 和 Clang 3.5+ 可以愉快地编译那段代码(它可以像我预期的那样工作)。但是,当使用 MSVC 14 CTP5 时,我收到此错误,提示 foo 已被定义:

Error error C2995: 'unknown-type foo(F &&)': function template has already been defined c++-scratch main.cpp 15

所以我的问题是:“懒人的 enable_if”是合法的 C++ 还是 MSVC 错误?

最佳答案

[temp.over.link]/6指定两个函数模板声明何时为重载。这是通过定义两个函数模板的等效性来完成的,如下所示:

Two function templates are equivalent if they [..] have return types [..] that are equivalent using the rules described above to compare expressions involving template parameters.

“上述规则”是

Two expressions involving template parameters are considered equivalent if two function definitions containing the expressions would satisfy the one definition rule (3.2) [..]

[basic.def.odr]/6 中说明了与这部分相关的 ODR那个

Given such an entity named D defined in more than one translation unit, then

  • each definition of D shall consist of the same sequence of tokens;

显然,由于返回类型(根据 [dcl.fct]/2 是尾随返回类型)不包含相同的标记,因此包含这些表达式的两个函数定义将违反 ODR。
因此 foo 的声明声明了非等效的函数模板并重载了名称。

您看到的错误是由于 VC++ 缺乏对表达式 SFINAE 的支持而发出的 - 大概是未检查尾随返回类型的等效性。


解决方法

您可以通过另一种方式使函数模板不等效 - 更改模板参数列表。如果你像这样重写第二个定义:

template <typename F, int=0>
auto foo(F&& f) -> decltype(f(0, 1), void())
{
    std::cout << "2" << std::endl;
}

然后是VC++ compiles it fine . 我缩短了 [temp.over.link]/6 中的引用,其中包括:

Two function templates are equivalent if they are declared in the same scope, have the same name, have identical template parameter lists [..]

其实,为了能够轻松引入新的重载,你可以使用一个小助手:

template <int I>
using overload = std::integral_constant<int, I>*;

用法例如

// Remember to separate > and = with whitespace
template <typename... F, overload<0> = nullptr>
auto foo(F&&... f) -> decltype(f(0, 1)..., void())

template <typename... F, overload<1> = nullptr>
auto foo(F&&... f) -> decltype(f(0, 1, 2)..., void())

Demo .

关于c++ - "lazy man' s enable_if"是合法的 C++ 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28203981/

相关文章:

c++ - 与竞争特定内存地址(互斥锁暂停另一个)相比,circularBuffer 如何提高性能?

c++ - 如何正确使用BOOST_THROW_EXCEPTION?

c++ - 采用右值参数的函数

c++ - 如何防止连续循环 #include 语句?

c++ - 在运行时可以检测到 C++03 和 C++11 之间的哪些差异(如果有)?

c++ - operator new() 可以在构造函数运行之前初始化 POD 吗?

C++ 数组删除运算符语法

c++ - 线程安全的复制构造函数/赋值运算符

c++ - 在 C++ (WLANAPI) 中获取 Windows wifi 托管网络设备的 ip 地址

visual-c++ - _configthreadlocale 和 localeconv