我尝试在Visual Studio 2013中编译以下程序并得到C2686: cannot overload static and non-static member functions
错误。
#include <iostream>
#include <type_traits>
struct foo
{
template<bool P>
static std::enable_if_t<P> bar()
{
std::cerr << "P is true\n";
}
template<bool P>
std::enable_if_t<!P> bar()
{
std::cerr << "P is false\n";
}
};
int main()
{
foo f;
f.bar<true>();
}
我熟悉这个编译器错误 - 请参阅this StackOverflow answer,但是很惊讶地看到错误与SFINAE结合,编译器总是会丢弃过载集中的两个重载中的一个。
Visual Studio 2013是否正确遵循此处的标准,或者是否可以与SFINAE一起使静态重载?
编辑:上面的对比示例,返回类型重载
如果没有SFINAE,您不能在static
上重载,并且您也不能在返回类型上重载。但是,Visual Studio 2013支持与SFINAE一起在返回类型上重载。
以下程序与上面的程序相同但删除static
并更改第二个foo::bar
声明的返回类型,正确编译。
#include <iostream>
#include <type_traits>
struct foo
{
template<bool P>
std::enable_if_t<P> bar()
{
std::cerr << "P is true\n";
}
template<bool P>
std::enable_if_t<!P, int> bar()
{
std::cerr << "P is false\n";
return 42;
}
};
int main()
{
foo f;
f.bar<true>();
}
在我看来,Visual Studio 2013会让这两个案例中的一个出错,但我希望语言律师可以提供明确的答案。
答案 0 :(得分:5)
令人惊讶的是,MSVC是正确的。 (我知道,震惊。)[over.load] / p1-2:
1并非所有函数声明都可以重载。这里指定了那些不能重载的东西。一个 如果程序在同一范围内包含两个这样的不可重载的声明,则程序格式不正确。 [注意:这个 限制适用于范围中的显式声明,以及此类声明和声明之间的声明 通过 using-declaration (7.3.3)。它不适用于由于名称而制作的函数集 查找(例如,由于 using-directives )或重载决策(例如,对于操作员功能)。 - 结束记录]
2某些函数声明不能重载:
- 只能在返回类型上有所不同的函数声明不能重载。
- 具有相同名称和相同的成员函数声明 如果 parameter-type-list 中的任何一个是
static
成员函数声明(9.4),则不能重载它。同样,成员函数 模板声明具有相同的名称,相同 parameter-type-list ,如果其中任何一个是static
成员函数模板,则不能重载相同的模板参数列表 宣言。 [...]- [...]
两个bar()
声明具有相同的名称,相同的 parameter-type-list 和相同的模板参数列表,其中至少有一个是static
,因此不能超载。
答案 1 :(得分:1)
我认为在这种情况下Visual Studio是不正确的。
[1]状态编译函数调用分两步进行,名称查找,然后在必要时进行重载解析(在这种情况下我们将不会看到)。
名称查找生成候选函数列表。名称查找由两个步骤组成,即依赖于参数的查找,然后是模板参数推导。
如果名称查找生成多个可能的函数调用,则会发生重载解析以确定要调用的正确函数。
SFINAE是一种元编程技术,用于在模板参数推导的子步骤中操纵候选函数集[2]。 SFINAE在模板参数替换期间引发替换错误,这阻止了将所述函数添加到候选集[3]。
让我们手动编译这个例子。
编译器需要解析
的函数调用f.bar();
首先,名称查找发生
2.1。参数依赖查找运行。由于没有参数,这一步骤不会减少候选人名单。
2.2。模板参数推断运行。
2.2.1。模板参数替换运行。这将P值替换为每个enable_if_t&lt;&gt;表达。 enable_if_t&LT;&GT;当它的谓词表达式(此处为P)为假时,会生成替换失败。因此,从候选列表中移除P引起替换失败的函数。在模板参数替换之后,该代码只能产生1个候选函数,因为enable_if_t&lt;&gt;表达是互斥的。
在模板参数替换步骤完成之前,Visual Studio似乎正在检查重载规则。如果它已经运行模板参数替换,那么由于候选列表包含单个函数,因此永远不会发生重载解析。