考虑这个例子:
#include <iostream>
struct Thing
{
Thing(int const& ref) : ref(ref) {}
int const& ref;
};
int main()
{
int option_i = 1;
short option_s = 2;
Thing thing_i(option_i);
Thing thing_s(option_s);
std::cout << thing_i.ref << "\n"; // 1
std::cout << thing_s.ref << "\n"; // 2
option_i = 10;
option_s = 20;
std::cout << thing_i.ref << "\n"; // 10
std::cout << thing_s.ref << "\n"; // 2 <<< !!!
}
现在,我理解WHY就是这种情况:使用临时对象将short
option_s
转换为int
。然后将对该临时值的引用传递给构造函数Thing::Thing
,即等效于
// Thing thing_s(option_s);
int __temporary = option_s;
Thing thing_s(__temporary);
然而,在许多情况下这可能是不可取的,因为很难发现差异。 在这种情况下,临时至少还活着, 但这也很容易成为暂时的临时参考。
您是否知道有任何方法(设计模式,gcc编译器选项,静态分析工具或其他方法)来检测这种const-reference-to-temporary案例?
为了检测这种情况,你会牺牲常态吗?
答案 0 :(得分:3)
只需编写另一个构造函数并将其标记为=delete
:
template<typename T>
Thing(T const &) = delete;
现在,如果你传递除<{1}}之外的任何参数,你将收到编译错误。
int
如果您只想删除 Thing thing_s(option_s); //error
(或一组已知类型),那么您可以使用:
short
希望能给你一个基本的想法。
答案 1 :(得分:1)
为绑定引用的类定义构造函数的好方法是通过地址获取参数:
struct Foo
{
Bar const & ref;
Foo(Bar const * b) : ref(*b) {}
};
用法:
Bar x;
Foo y(&x); // OK
Foo z(&Bar{}); // error: cannot take address of prvalue
这要求用户不遗余力地获取prvalue的地址,从而清楚地表明您期望已经存在的对象的地址,并且可能继续超过您的实例的生命周期。