Tra*_*kel 50 c++ sfinae visual-c++ language-lawyer c++11
我经常使用一种技术,我称之为"懒人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;
}
Run Code Online (Sandbox Code Playgroud)
随着--std=c++11
,g ++ 4.7+和Clang 3.5+愉快地编译那段代码(并且它按照我的预期工作).但是,当使用MSVC 14 CTP5时,我得到这个错误抱怨foo
已经定义:
错误错误C2995:'unknown-type foo(F &&)':函数模板已经定义了c ++ - scratch main.cpp 15
所以我的问题是:"懒人enable_if
"是合法的C++还是这是一个MSVC错误?
Col*_*mbo 44
[temp.over.link]/6指定两个函数模板声明何时重载.这是通过定义两个函数模板的等效性来完成的,如下所示:
两个函数模板是等价如果他们[..]有返回类型[..]对等同使用上述规则比较涉及模板参数表达式.
"上述规则"是
如果包含表达式的两个函数定义满足一个定义规则(3.2)[...],则涉及模板参数的两个表达式被认为是 等效的
与此部分相关的ODR在[basic.def.odr]/6中说明
鉴于这样的实体名称
D
在多个翻译单元中定义,那么
- 每个定义
D
应由相同的令牌序列组成 ;
显然,由于返回类型(根据[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;
}
Run Code Online (Sandbox Code Playgroud)
然后VC++ 编译好了.我缩短了[temp.over.link]/6中的引用,其中包括:
如果两个函数模板在同一范围内声明,具有相同的名称,具有相同的模板参数列表 [...] ,则它们是等效的
事实上,为了能够轻松引入新的重载,您可以使用一个小帮手:
template <int I>
using overload = std::integral_constant<int, I>*;
Run Code Online (Sandbox Code Playgroud)
用法是例如
// 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())
Run Code Online (Sandbox Code Playgroud)
演示.
Jam*_*lis 33
这是一个名为"表达SFINAE"的功能. Visual C++尚未完全支持它(请参阅"VS 2015预览版中的C++ 11/14/17功能",了解截至本答复时的最新一致性更新).