C++按返回类型重载函数
C++ overload function by return type
如果我认为我对C++有所了解,那就是你不能按返回类型重载函数。
有人能解释一下这里发生了什么吗?
class A { public: typedef int _foo; };
class B {};
template<class T>
typename T::_foo Foo(int)
{
cout << "Foo(int)n"; return typename T::_foo();
}
template<class T>
typename T Foo(char)
{
cout << "Foo(char)n"; return typename T();
}
int main()
{
Foo<A>(0); // Writes "Foo(int)", as expected.
Foo<B>(0); // Writes "Foo(char), expected error unable to compile template.
return 0;
}
有两个类A和B。A定义typedef_foo,B不定义。函数模板Foo有两个重载,Foo(int)和Foo(char)。Foo(int)返回T::_Foo,Foo(char)返回T.
Foo(0)随后被调用两次。这是Foo(int)的精确匹配,所以我期望Foo<A>(0)编译ok,并且Foo<B>(0)编译失败,因为B没有定义模板中使用的类型_foo。
实际发生的是Foo<B>(0)完全忽略Foo(int),而是实例化Foo(char)。但是,根据重载解析的正常规则,Foo(0)显然与Foo(int)完全匹配,唯一使Foo(char)更可行的匹配是不应考虑的返回类型。
要验证影响过载分辨率的是返回值,只需添加以下内容:
template<class T>
void Bar(int) { typename T::_foo a; cout << "Bar(int)n"; }
template<class T>
void Bar(char) { cout << "Bar(char)n"; }
Bar<A>(0); // Writes "Bar(int), as expected.
//Bar<B>(0); // Error C2039: '_foo' : is not a member of 'B', as expected.
这清楚地表明,在没有返回值的情况下,Foo(int)确实是正确的重载,并且如果模板无法解析其模板参数中使用的类型,则编译失败是正常的结果。
您没有重载返回类型,而是专门化了一个函数模板,当Foo<B>(int)
专门化形成无效类型B::_foo
时,该专门化将从SFINAE设置的重载中删除,使Foo<B>(char)
函数成为唯一可行的函数。
更详细地说,对Foo<A>(0)
的调用首先执行名称查找以查找范围中的所有Foo
名称,然后实例化任何函数模板以查找重载候选者,然后重载解析选择最佳匹配。
实例化函数模板的步骤产生以下两个函数声明:
int Foo<A>(int);
A Foo<A>(char);
过载分辨率选择第一个作为最佳匹配。
然而,当调用Foo<B>(0)
时,实例化会产生以下声明:
<invalid type> Foo<B>(int);
B Foo<B>(char);
第一个声明是无效的,所以只有一个重载解析的候选者,所以就是被调用的那个。
在Bar
示例中,实例化过程中形成的无效类型不在函数声明的"直接上下文"中(它在函数定义中,即主体中),因此SFINAE不适用。
template<class T>
typename T::_foo Foo(int);
template<class T>
typename T Foo(char);
因此,您的代码声明了这个重载函数。太好了。
Foo<A>(0);
在这种情况下,编译器试图为上面声明的原型填写模板,该模板将是:
int Foo(int);
A foo(char);
由于您将一个整数作为参数传递,所以第一个参数更匹配,因此编译器使用该参数。
Foo<B>(0);
编译器再次看到这一行,并试图填写原型的模板,但是。。。
WTFDOESNTMAKESENSE?!?!? Foo(int);
A foo(char);
很明显,第一个甚至没有意义,所以它放弃了它,使用了第二个重载。这实际上与返回类型无关,而是与在决定你指的是哪个函数之前如何填写模板原型有关。以下是您重新排列的示例以澄清:
template<class T>
int foo(T::_foo) {}
template<class T>
int foo(char) {}
int main() {
foo<A>(0); //uses the first, `int foo(int)` better than `int foo(char)`
foo<B>(0); //uses the second, because the first doesn't work with B.
这被称为SFINAE,请注意,它只适用于模板参数、返回类型和函数参数中非常的特定情况,但不适用于函数体本身。这就是为什么你的"验证"会导致错误,因为它无法从原型中判断出其中一个函数是无效的,而在决定重载时,原型是唯一考虑的因素。
- 为什么使用SFINAE而不是函数重载
- 为什么我不能在 C++ 中的特定函数重载中调用同一函数的任何其他重载?
- c++:可变模板和函数重载
- 在缺少函数重载时抛出异常,并带有 std::variant 而不是编译时错误
- 解决模板成员函数重载
- 为什么不允许成员函数和非成员函数之间的函数重载?
- 推断模板化函数中的函数重载
- C++复制函数重载导致"must be a nonstatic member function"错误
- 为什么 std::sort 找不到合适的(静态成员)函数重载?
- 可变参数泛型 lambda 和函数重载
- C++中的函数重载和继承
- 当有右值构造函数可用时,为什么从右值调用类引用构造函数重载?
- C/C++ 可变参数宏函数重载
- 将基类的成员函数重载到其他派生类C++
- C++ 函数重载匹配
- C++函数重载,具体步骤是什么
- C++:使用 param pack 显式调用函数重载
- 隐式生成的函数重载用于右值参数?
- 使用函数重载输入运算符
- 运算符重载函数上的函数重载