这段代码中有一些不太明显的事情发生:
float a = 1.;
const float & x = true ? a : 2.; // Note: `2.` is a double
a = 4.;
std::cout << a << ", " << x;
clang 和 gcc 输出:
4, 1
人们会天真地期望打印两次相同的值,但事实并非如此。这里的问题与引用无关。有一些有趣的规则规定了 的类型? :
。如果两个参数的类型不同并且可以强制转换,则它们将使用临时参数。引用将指向 的临时? :
.
上面的例子编译得很好,根据你的编译器版本,它可能会在使用 -Wall
编译时发出警告,也可能不会发出警告。
下面是一个例子,说明在看起来合法的代码中很容易出错:
template<class Iterator, class T>
const T & min(const Iterator & iter, const T & b)
{
return *iter < b ? *iter : b;
}
int main()
{
// Try to remove the const or convert to vector of floats
const std::vector<double> a(1, 3.0);
const double & result = min(a.begin(), 4.);
cout << &a[0] << ", " << &result;
}
如果您在此代码之后的逻辑假设 a[0]
上的任何更改将反射(reflect)到 result
,那么在 ? 的情况下将是错误的:
创建一个临时的。此外,如果您在某个时候创建了一个指向 result
的指针,并且在 result
超出范围后使用它,那么尽管您原来的 a
没有超出范围。
除了提到的“可维护性和阅读问题”here 之外,我认为不使用此表单的理由很充分。尤其是在编写模板代码时,您的某些类型及其 const'ness 可能超出您的控制范围。
所以我的问题是,在三元运算符上使用 const &
是否安全?
附:奖励示例 1,额外的复杂性(另见 here):
float a = 0;
const float b = 0;
const float & x = true ? a : b;
a = 4;
cout << a << ", " << x;
叮当输出:
4, 4
gcc 4.9.3 输出:
4, 0
使用 clang,这个例子可以按预期编译和运行,但使用最新版本的 gcc (
P.S.2 Bonus 示例 2,非常适合面试 ;) :
double a = 3;
const double & a_ref = a;
const double & x = true ? a_ref : 2.;
a = 4.;
std::cout << a << ", " << x;
输出:
4, 3
最佳答案
首先,条件运算符的结果要么是指定所选操作数的左值,要么是其值来自所选操作数的纯右值。
T.C. 指出的异常:如果至少一个操作数是类类型并且具有转换为引用的运算符,则结果可能是一个左值,指定由该运算符的返回值指定的对象;如果指定的对象实际上是一个临时对象,则可能会导致悬空引用。这是提供纯右值到左值的隐式转换的此类运算符的问题,而不是条件运算符本身引入的问题。
在这两种情况下,将引用绑定(bind)到结果是安全的,将引用绑定(bind)到左值或纯右值的通常规则适用。如果引用绑定(bind)到纯右值(条件的纯右值结果,或者从条件的左值结果初始化的纯右值),则纯右值的生命周期将延长以匹配引用的生命周期。
在你原来的情况下,条件是:
true ? a : 2.
第二个和第三个操作数是:“float
类型的左值”和“double
类型的纯右值”。这是 cppreference summary 中的案例 5 ,结果是“double
类型的纯右值”。
然后,您的代码使用不同(非引用相关)类型的纯右值初始化 const 引用。这样做的行为是复制初始化与引用相同类型的临时对象。
总之,在 const float & x = true 之后? a : 2.;
, x
是一个左值,表示 float
,其值是将 a
转换为 的结果双
并返回。 (不确定是否保证与 a
相等)。 x
未绑定(bind)到 a
。
在额外情况 1 中,条件运算符的第二个和第三个操作数是“float
类型的左值”和“const float
类型的左值”。这是同一个 cppreference 链接的案例 3,
both are glvalues of the same value category and have the same type except for cv-qualification
行为是第二个操作数被转换为“const float
类型的左值”(表示同一个对象),条件的结果是“const float类型的左值”
"表示选中的对象。
然后将 const float &
绑定(bind)到“const float
类型的左值”,直接绑定(bind)。
所以在 const float & x = true 之后? a : b;
, x
直接绑定(bind)到 a
或 b
。
在额外情况 2 中,true ? a_ref : 2.
.第二个和第三个操作数是“const double
类型的左值”和“double
类型的纯右值”,所以结果是“double
类型的纯右值” >”。
然后将它绑定(bind)到 const double & x
,这是一个直接绑定(bind),因为 const double
与 double
是引用相关的。
那么在 const double & x = true 之后? a_ref : 2.;
,则 x
是一个左值,表示与 a_ref
具有相同值的 double (但 x
不是绑定(bind)到 a
)。
关于c++ - 在 C++ 中创建对三元运算符结果的 const 引用是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40167231/