问题 为什么非模板化函数具有相同的名称和参数但返回类型不同是非法的? (但模板功能合法吗?)


我看了几个相关的堆栈溢出线程,如 这种模板函数重载的情况不符合我的理解

函数重载按返回类型?

但似乎都没有给我我正在寻找的答案,至少不是以一种我很容易解释的方式。

我的问题归结为:从设计和技术的角度来看,为什么这样做是合法的:

#include <iostream>

using namespace std;

template<class T>
void func(){
    cout << "Compiler inferred from void return value!\n";
}

template<class T>
int func(){
    cout << "Compiler inferred from int return value!\n";
    return 0;
}

int main(){
    void (*thisFunc)()=func<int>;
    int (*thatFunc)()=func<int>;
    thisFunc();
    thatFunc();
}

但不是这个:

#include <iostream>

using namespace std;

void func(){
    cout << "You won't see this because it won't compile!\n";
}

int func(){
    cout << "Nor this one!\n";
    return 0;
}

int main(){
    void (*thisFunc)()=func;
    int (*thatFunc)()=func;
    thisFunc();
    thatFunc();
}

值得一提的是,如果我在初始化thisFunc和thatFunc时没有明确地给func一个任意的模板参数,那么第一个例子就不会编译。两者之间唯一的另一个区别是第一个有func的函数是一个完全无关紧要的类型,除了它显然允许我通过返回类型重载。在这种情况下,编译器甚至能够对要调用的函数进行推断,使得c ++中返回类型的函数重载的非法性对我来说有些困惑。无论如何,我猜这是一个巧妙的技巧。

编辑:最困扰我的是不一致性:在我真正考虑通过返回类型进行重载之前,我会想很长很难,但如果对某人进行“修复” 希望 通过返回类型重载是简单地添加一个无意义的模板参数或显然将函数包装在命名空间内,然后我认为C ++应该或多或少地严格关于它认为不明确的内容。是否有令人信服的理由为什么模板和命名空间需要此功能才能正常工作?例如,如果不允许模板在我的示例中消除代码歧义,那么是否有一个可行的代码无法工作的示例?我从语言设计的角度提出要求,而不是编译器合规性的观点。


7784
2017-08-24 09:42


起源

对于模板,我认为是允许SFINAE或返回类型。 - Jarod42
您实际上并没有在返回类型上重载,而是使用SFINAE,因此为每个函数指针选择唯一可能的模板实例化。当你使用 func<int>,在每个声明中,只有一个替换有效的模板,但是另一个失败并不是错误。它被忽略了。 - Bo Persson


答案:


它通常不是无效的:有一种方法可以在返回类型上重载非模板函数。它只会像你做的那样无效。

namespace A {
  void func() {}
}
namespace B {
  int func() { return 0; }
}
using A::func;
using B::func;
int main(){
  void (*thisFunc)()=func;
  int (*thatFunc)()=func;
  thisFunc();
  thatFunc();
}

这编译,链接和运行良好。

由于这是有效的并且被实际实现所接受,因此应该清楚的是,没有技术原因导致您的代码无效。不可否认,它需要对名称修改进行一些更改,因为通常非模板函数在修复的名称中没有返回类型,但它很容易实现。

剩下的是一个合乎逻辑的原因:在返回类型上重载非模板函数几乎肯定是一个错误。它使得函数在正常情况下不可调用,它会对常见错误进行极差的诊断(在一个地方更改声明而忘记其他地方),它的使用极其有限,所以就是不要这样做。


11
2017-08-24 11:48