静态重载与 SFINAE 结合使用
Overloading-on-static in conjunction with SFINAE
我在 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 Whosebug answer,但我很惊讶地看到这个错误与 SFINAE 一起使用,编译器将始终丢弃重载集中的两个重载之一。
Visual Studio 2013 是否正确遵循此处的标准,或者是否可以与 SFINAE 一起在 static 上重载?
编辑:将上面的示例与 return 类型
上的重载进行对比
没有 SFINAE,您不能在 static
上重载,也不能在 return 类型上重载。但是,Visual Studio 2013 支持与 SFINAE 一起重载 return 类型。
以下程序与上面的程序相同,但删除了 static
并更改了第二个 foo::bar
声明的 return 类型,编译正确。
#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年这两个案例中有一个是错误的,但我希望语言律师能够提供一个明确的答案。
令人惊讶的是,MSVC 是正确的。 (我知道,震惊。)[over.load]/p1-2:
1 Not all function declarations can be overloaded. Those that cannot be overloaded are specified here. A
program is ill-formed if it contains two such non-overloadable declarations in the same scope. [Note: This
restriction applies to explicit declarations in a scope, and between such declarations and declarations made
through a using-declaration (7.3.3). It does not apply to sets of functions fabricated as a result of name
lookup (e.g., because of using-directives) or overload resolution (e.g., for operator functions). —end note]
2 Certain function declarations cannot be overloaded:
- Function declarations that differ only in the return type cannot be overloaded.
- Member function declarations with the same name and the same
parameter-type-list cannot be overloaded if any of them is a
static
member function declaration (9.4). Likewise, member function
template declarations with the same name, the same
parameter-type-list, and the same template parameter lists cannot be overloaded if any of them is a static
member function template
declaration. [...]
- [...]
两个bar()
声明具有相同的名称,相同的参数类型列表,以及相同的模板参数列表,其中至少有一个是static
,因此不能重载。
背景
我认为 Visual Studio 在这种情况下是不正确的。
[1] 指出编译函数调用分两步进行,名称查找,然后是重载解析,如果需要(我们将在这种情况下看到没有必要)。
名称查找生成候选函数列表。名称查找由两个步骤组成,Argument-dependent lookup,然后是模板参数推导。
如果名称查找生成多个可能的函数调用,则会进行重载解析以确定要调用的正确函数。
SFINAE 是一种元编程技术,用于在模板参数推导的子步骤中操纵候选函数集 [2]。 SFINAE 在模板参数替换期间引入替换错误,这会阻止将所述函数添加到候选集 [3]。
例子
让我们手动编译这个例子。
编译器需要解析
的函数调用
f.bar();
首先,进行名称查找
2.1。参数依赖查找 运行s。由于没有参数,此步骤不会减少候选列表。
2.2。模板参数推导 运行s.
2.2.1。模板参数替换 运行s。这会将 P 值代入每个 enable_if_t<> 表达式。 enable_if_t<> 在其谓词表达式(此处为 P)为假时生成替换失败。因此,从候选列表中删除 P 导致替换失败的函数。模板参数替换后,此代码只能产生 1 个候选函数,因为 enable_if_t<> 表达式是互斥的。
结论
看来 Visual Studio 在模板参数替换步骤完成之前正在检查重载规则。如果它有 运行 模板参数替换,那么重载决策将永远不会发生,因为候选列表包含一个函数。
参考资料
- [1] http://en.cppreference.com/w/cpp/language/overload_resolution
- [2] http://en.cppreference.com/w/cpp/language/template_argument_deduction
- [3] http:// en.cppreference.com /w /cpp /language/sfinae
我在 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 Whosebug answer,但我很惊讶地看到这个错误与 SFINAE 一起使用,编译器将始终丢弃重载集中的两个重载之一。
Visual Studio 2013 是否正确遵循此处的标准,或者是否可以与 SFINAE 一起在 static 上重载?
编辑:将上面的示例与 return 类型
上的重载进行对比没有 SFINAE,您不能在 static
上重载,也不能在 return 类型上重载。但是,Visual Studio 2013 支持与 SFINAE 一起重载 return 类型。
以下程序与上面的程序相同,但删除了 static
并更改了第二个 foo::bar
声明的 return 类型,编译正确。
#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年这两个案例中有一个是错误的,但我希望语言律师能够提供一个明确的答案。
令人惊讶的是,MSVC 是正确的。 (我知道,震惊。)[over.load]/p1-2:
1 Not all function declarations can be overloaded. Those that cannot be overloaded are specified here. A program is ill-formed if it contains two such non-overloadable declarations in the same scope. [Note: This restriction applies to explicit declarations in a scope, and between such declarations and declarations made through a using-declaration (7.3.3). It does not apply to sets of functions fabricated as a result of name lookup (e.g., because of using-directives) or overload resolution (e.g., for operator functions). —end note]
2 Certain function declarations cannot be overloaded:
- Function declarations that differ only in the return type cannot be overloaded.
- Member function declarations with the same name and the same parameter-type-list cannot be overloaded if any of them is a
static
member function declaration (9.4). Likewise, member function template declarations with the same name, the same parameter-type-list, and the same template parameter lists cannot be overloaded if any of them is astatic
member function template declaration. [...]- [...]
两个bar()
声明具有相同的名称,相同的参数类型列表,以及相同的模板参数列表,其中至少有一个是static
,因此不能重载。
背景
我认为 Visual Studio 在这种情况下是不正确的。
[1] 指出编译函数调用分两步进行,名称查找,然后是重载解析,如果需要(我们将在这种情况下看到没有必要)。
名称查找生成候选函数列表。名称查找由两个步骤组成,Argument-dependent lookup,然后是模板参数推导。
如果名称查找生成多个可能的函数调用,则会进行重载解析以确定要调用的正确函数。
SFINAE 是一种元编程技术,用于在模板参数推导的子步骤中操纵候选函数集 [2]。 SFINAE 在模板参数替换期间引入替换错误,这会阻止将所述函数添加到候选集 [3]。
例子
让我们手动编译这个例子。
编译器需要解析
的函数调用f.bar();
首先,进行名称查找
2.1。参数依赖查找 运行s。由于没有参数,此步骤不会减少候选列表。
2.2。模板参数推导 运行s.
2.2.1。模板参数替换 运行s。这会将 P 值代入每个 enable_if_t<> 表达式。 enable_if_t<> 在其谓词表达式(此处为 P)为假时生成替换失败。因此,从候选列表中删除 P 导致替换失败的函数。模板参数替换后,此代码只能产生 1 个候选函数,因为 enable_if_t<> 表达式是互斥的。
结论
看来 Visual Studio 在模板参数替换步骤完成之前正在检查重载规则。如果它有 运行 模板参数替换,那么重载决策将永远不会发生,因为候选列表包含一个函数。
参考资料
- [1] http://en.cppreference.com/w/cpp/language/overload_resolution
- [2] http://en.cppreference.com/w/cpp/language/template_argument_deduction
- [3] http:// en.cppreference.com /w /cpp /language/sfinae