小议C++函数签名与模板返回类型

题记:什么事情都要追问一个为什么,真正理解了为什么,才能活学活用。

代码1

下面的代码能编译通过吗?

#include 
#include 

class X
{
public:
    int *get() { return new int(); }
    double *get() { return new double(); }
};

int main()
{
    int * v1 = X().get();
    double * v2 = X().get();
    return 0;
}

答案肯定是编译不过。因为下面两个函数的“签名”是一样的:

    int *get();
    double *get();

在 C++ 语言中,函数签名包含函数名称、函数参数类型、函数参数个数等信息,但是不包含返回值类型。

代码2

下面的代码能编译通过吗?

#include 
#include 

class X
{
public:
  template 
  T *get() { return new T(); }
};

int main()
{
  int * v1 = X().get();
  double * v2 = X().get();
  return 0;
}

答案是可以编译通过!这是为什么呢????难道 X 这个模板对象展开后,不是展开成类似下面的样子吗:

class X
{
public:
    int *get() { return new int(); }
    double *get() { return new double(); }
};

按照 代码1 的分析和结论,这样是编译不过的才对呀!

解惑

C++ 标准里是明确定义了,函数签名不包含返回值类型。但是,为什么要这样定义呢?原因如下:

虽然,下面的调用理论上编译器是可以帮忙选出正确函数:

    int * v1 = X().get();
    double * v2 = X().get();

但是,下面的场景编译器就会犯糊涂:

X().get();

不使用返回值的调用方式 C++ 是允许的,但是这种情况下,编译器应该为它链接哪一个函数呢?编译器也不知道!正是因为存在这种不好解析的场景存在,C++才把“返回值类型”从函数签名中剔除。

明白了这个道理,就不难理解 ** 代码2 ** 为何可以编译通过了。下面的调用不存在任何二义性,所以编译器才允许模板函数中使用模板参数定义返回值类型。

    X().get<int>();
    X().get<double>();

使用 nm 工具可以看到,编译之后类型信息也加入到符号中了:
在这里插入图片描述
小议C++函数签名与模板返回类型_第1张图片

你学废了吗?

补充资料

并不是所有语言都不允许返回值类型作为函数签名的。比如 Swift 语言。那么它是如何解决二义性问题的呢?很简单,针对 X().get() 这种调用直接报错!除非提供足够信息帮助编译器正确推导出完整签名。

func some() -> Bool {
    return true;
}

func some() -> Int {
    return 1;
}

// 编译成功,编译器可以找到正确的函数
let valBool: Bool = some()
let valInt: Int = some()

// 编译失败,编译器无法确定应该使用哪个函数
some()

参考资料:Stack Overflow: Why is the return type of method not included in the method-signature?

你可能感兴趣的:(c++,开发语言)