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'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)到 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/40167231/

相关文章:

c++ - `typedef enum {} t` 是否允许 C++0x 中的作用域枚举元素标识符?

git - 解决gcc中undefined reference library linking error

c - 为什么我的 Thrift (c_glib) 示例无法使用 "error: invalid conversion"进行编译?

c++ - 如何验证函数不调用自身?

c++ - 返回同时存在于列表 A 和列表 B 中的 x,y 坐标的最快方法是什么?

c++ - c++ 中 std::next_permutation() 函数的时间复杂度是多少?

c++ - 检查由 3 个点定义的角是内角还是外角

c++ - 减少通过 UDP 套接字发送的数据

c++ - 我可以将 final 关键字应用于 C++11 中的 POD(标准布局)结构吗?我是不是该?

c++ - 对封闭类成员的嵌套类访问