c++ - 在 C++ 中创建对三元运算符结果的 const 引用是否安全?

标签 c++ c++11 gcc ternary-operator clang++

这段代码中发生了一些不明显的事情:

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 & 是否安全?

附言奖励示例 1,额外的并发症(另见 here ):

float a = 0;
const float b = 0;
const float & x = true ? a : b;

a = 4;
cout << a << ", " << x;

clang 输出:

4, 4

gcc 4.9.3 输出:

4, 0

使用 clang 这个例子可以按预期编译和运行,但是使用最新版本的 gcc (

P.S.2 Bonus example 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 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)到 ab


在奖励案例 2 中,true ? a_ref : 2. 。第二个和第三个操作数是“const double 类型的左值”和“double 类型的纯右值”,所以结果是“double 类型的纯右值” >”。

然后将它绑定(bind)到 const double & x,这是一个直接绑定(bind),因为 const doubledouble 是引用相关的。

那么在 const double & x = true 之后呢? a_ref : 2.; ,则 x 是一个左值,表示与 a_ref 具有相同值的 double 值(但 x 不是绑定(bind)到 a)。

关于c++ - 在 C++ 中创建对三元运算符结果的 const 引用是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52295148/

相关文章:

c++ - 通过 BOOST_FOREACH 使我的 C++ 类可迭代

c++ - 如何排序 std::vector 忽略某些数字?

c++ - 在多线程中初始化 vector

在 gcc 编译的 c 程序中,表达式是否太长?

c++ - gcc 选项 "-fmessage-length"是什么意思?

c++ - 在 C++ 中返回字符串的最佳方法是什么?

c++ - 如何打印中间包含 0 字符的字符串?

c++ - 如何成功解耦这些类

c++ - 实时获取 QProcess 的响应

c - 这是拦截系统调用的好方法吗?