使用引用作为模板类型

Using reference as template type

本文关键字:类型 引用      更新时间:2023-10-16

在下面的示例中,Foo没有做预期的事情,但我无法弄清楚为什么允许编译它。

#include <string>
#include <iostream>
typedef std::string& T;
T Foo(int & i)
{
return T(i);
}
int main()
{
int a = 1;
std::string & s = Foo(a);
}

我在模板中发现了这一点,但typedef显示它与模板无关。 不用说,s在这里不是一个有效的字符串。 我认为在返回Foo时构造值会产生编译错误。

我在这里错过了什么?

首先,这个问题实际上与模板无关,这是毫无价值的,因为这段代码编译得很好:

typedef std::string& T;
T Foo(int& i) {
return T(i);
}

我认为这编译的原因是return语句等效

return reinterpret_cast<T>(i);

万一T碰巧是参考成员。当然,这编译:你答应你知道你在做什么,并要求编译器相信你。

好的,在 5.2.3 [expr.type.conv] 第 1 段中找到它:

。如果表达式列表是单个表达式,则类型转换表达式等效(在定义性上,如果在含义上定义)与相应的强制转换表达式 (5.4) 等效。...

。以及 5.4 [Expr.cast] 第 4 段:

由 [其他形式的强制转换] reinterpret_cast (5.2.10) [...] 执行的转换可以使用显式类型转换的强制转换表示法来执行。[...]

(省略涵盖涉及用户定义类型、内置类型转换const转换等的情况)

这与模板无关,如果T只是std::string&的typedef而不是推导的模板参数,你会得到相同的结果:

#include <string>
typedef std::string& T;
T Foo(int & i)
{
return T(i);
}
int main()
{
int a = 1;
std::string & s = Foo(a);
}

Dietmar的回答让我意识到这可以进一步简化为:

#include <string>
typedef std::string& T;
int main()
{
int a = 1;
std::string & s = T(a);
}

其中T(a)与铸件相同(T)a(std::string&)a(根据 5.4 [expr.castconst_cast] 的规则)如果有效(它不是)或一个static_cast,如果有效(它不是),或者一个static_cast后跟一个const_cast,如果它是有效的(它不是),或者一个reinterpret_cast,如果它是有效的(它是),或者一个reinterpret_cast后跟一个const_cast,如果这是有效的, 否则表达式格式不正确。

所以正如迪特马尔所说,这和做一个reinterpret_cast是一样的,即

std::string & s = reinterpret_cast<std::string&>(a);

我觉得原始代码编译很令人惊讶,但由于它与上面的那行相同,因此允许编译。但是,使用强制转换的结果是未定义的行为。

为了避免T(a)等效于强制转换的意外情况,请使用新的 C++11 统一初始化语法T{a},它始终是初始化,而不是强制转换表达式。

很好的问题,调查和回答它向我展示了一个我以前不知道的新问题,感谢 JaredC 和 Dietmar 的新知识!