使用引用作为模板类型

Jar*_*edC 7 c++

在下面的示例中,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);
}
Run Code Online (Sandbox Code Playgroud)

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

我在这里错过了什么?

Die*_*ühl 6

首先,问题实际上与模板没有任何关系并不值得,因为这段代码编译得很好:

typedef std::string& T;
T Foo(int& i) {
    return T(i);
}
Run Code Online (Sandbox Code Playgroud)

认为这个编译的原因是该return陈述相当于

return reinterpret_cast<T>(i);
Run Code Online (Sandbox Code Playgroud)

以防T碰巧是参考成员.......当然,这就是编译:你答应过你知道自己在做什么,并要求编译人员相信你.

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

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

......和5.4 [expr.cast]第4段:

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

(这些部门涉及涉及用户定义类型,内置类型转换,const转换等的案例)

  • 或者使用`T {i}`,它总是初始化而不是强制转换 (3认同)

Jon*_*ely 5

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

#include <string>

typedef std::string& T;

T Foo(int & i)
{
    return T(i);
}

int main()
{
    int a = 1;
    std::string & s = Foo(a);
}
Run Code Online (Sandbox Code Playgroud)

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

#include <string>

typedef std::string& T;

int main()
{
    int a = 1;
    std::string & s = T(a);
}
Run Code Online (Sandbox Code Playgroud)

其中T(a)相同铸造(T)a(std::string&)a它(按5.4 [expr.cast]的规则)将做一个const_cast,如果这是有效的(这是不)或static_cast如果这是有效的(这是不)或static_cast随后通过const_cast如果这是有效的(这是不)或reinterpret_cast如果这是有效的(这)或reinterpret_cast随后是const_cast如果这是有效的,否则是形成不良的表达.

正如Dietmar所说,这与做一个reinterpret_cast,即

std::string & s = reinterpret_cast<std::string&>(a);
Run Code Online (Sandbox Code Playgroud)

我发现原始代码编译时非常令人惊讶,但由于它与上面的那行相同,所以允许编译.但是,使用强制转换的结果是未定义的行为.

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

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