在C ++中创建一个三元运算符结果的const引用是否安全? [英] Is it safe to create a const reference to result of ternary operator in C++?

查看:176
本文介绍了在C ++中创建一个三元运算符结果的const引用是否安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这段代码中有一些非常明显的内容:

  float a = 1 .; 

const float& x =真? a2。; //注意:`2.`是双

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()
{
//尝试移除const或将其转换为float的向量
const std :: vector< double> ; a(1,3.0);

const double& result = min(a.begin(),4);

cout<< & a [0]<< ,<< &安培;结果;



$ b $ p
$ b

如果您的逻辑代码假定在 a [0] 将被反映为 result ,如果创建一个临时的。另外,如果在某些时候你指向 result ,并且在 result 超出了范围之后使用它,那么尽管您的原始 a 尚未超出范围,但这将成为细分故障。



我觉得有严重的原因,除了可维护性和阅读问题之外,不要使用这种形式。 here ,特别是在写模板代码时,你的某些类型和它们的常量可能会超出你的控制范围。

所以我的问题是,对三元运算符使用 const& s是否安全?



PS附加示例1,额外的并发症(另请参阅):

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

a = 4;
cout<< a<< ,<< X;

clang输出:

  4,4 

gcc 4.9.3输出:

  4,0 

用clang这个例子编译并按预期运行,但是最近版本的gcc(

PS2 Bonus example 2,非常适合面试;):

  double a = 3; 

const double& a_ref = a;

const double& x =真? a_ref:2。

a = 4。

std :: cout<< a<< ,<< X;

输出:

  4,3 


首先,条件运算符的结果是指定所选操作数的glvalue,或者其值来自所选操作数的prvalue。

异常由TC指出:如果至少有一个操作数是类类型的并且有一个转换为引用操作符,则结果可能是指定的左值该运算符的返回值指定的对象;并且如果指定的对象实际上是临时的,则可能导致悬挂参考。对于这样的运算符来说,这是一个问题,它将prvalues隐式转换为左值,而不是由条件运算符本身引入的问题。



在这两种情况下,绑定对结果的引用是安全的,通常绑定引用到左值或prvalue的规则适用。如果引用绑定到一个prvalue(或者是条件的prvalue结果,或者是从条件的左值结果初始化的prvalue),则会延长prvalue的生命周期以匹配引用的生命周期。






在您的原始案例中,条件为:

  true? a:2. 

第二个和第三个操作数是:左值类型 float 和类型 double 的前值。这是 cppreference概要中的情况5,其结果是类型的前值 double

然后,你的代码初始化一个具有不同(非参考值)相关)类型。这样做的行为是复制 - 初始化与引用相同类型的临时文件。
总之,在 const float& x =真? a:2。; x 是一个左值,表示 float ,其值是将 a 转换为 double 并返回。 (不知道是否保证比较等于 a )。 x 未绑定至 a






在奖金情况1中,条件运算符的第二个和第三个操作数是类型 float >的左值和类型<$的左值c $ c> const float 。这是相同cppreference链接的情况3,b
$ b


都是相同值类别的glvalues,除了cv-qualification

其行为是将第二个操作数转换为类型的左值const float (表示同一个对象),条件的结果是 const float 类型的左值,表示所选对象。



然后,将 const float& 绑定到 const float 类型的左值,直接绑定。

因此,在 const float& amp; x =真? a:b; x 直接绑定到 a b






在奖金情况2中, true? a_ref:2。。第二个和第三个操作数是类型 const double 和类型 double 的前值,所以结果是类型 double 的前值。

然后你将它绑定到 const double& x ,这是一个直接绑定,因为 const double 是与 double 有关的引用。

所以在 const double& x =真? a_ref:2; ,则 x 是一个左值,表示与 a_ref (但 x 不绑定到 a )。


There's something quite non-obvious going on in this code:

float a = 1.;

const float & x = true ? a : 2.; // Note: `2.` is a double

a = 4.;

std::cout << a << ", " << x;

both clang and gcc output:

4, 1

One would naively expect the same value printed twice but this isn't the case. The issue here has nothing to do with the reference. There are some interesting rules dictating the type of ? :. If the two arguments are of different type and can be casted, they will by using a temporary. The reference will point to the temporary of ? :.

The example above compiles fine and it might or might not issue a warning while compiling with -Wall depending on the version of your compiler.

Here's an example on how easy it's to get this wrong in legitimate-looking code:

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;
}

If your logic after this code assumes that any changes on a[0] will be reflected to result, it will be wrong in cases where ?: creates a temporary. Also, if at some point you make a pointer to result and you use it after result goes out of scope, there will be a segmentation fault despite the fact that your original a hasn't gone out of scope.

I feel there're serious reasons NOT to use this form beyond "maintainability and reading issues" mentioned here especially while writing templated code where some of your types and their const'ness might be out of your control.

So my question is, is it safe to use const &s on ternary operators?

P.S. Bonus example 1, extra complications (see also here):

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

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

clang output:

4, 4

gcc 4.9.3 output:

4, 0

With clang this example compiles and runs as expected but with up to recent versions of gcc (

P.S.2 Bonus example 2, great for interviews ;) :

double a = 3;

const double & a_ref = a;

const double & x = true ? a_ref : 2.;

a = 4.;

std::cout << a << ", " << x;

output:

4, 3

解决方案

First of all, the result of the conditional operator is either a glvalue designating the selected operand, or a prvalue whose value comes from the selected operand.

Exception as noted by T.C.: if at least one operand is of class type and has a conversion-to-reference operator, the result may be an lvalue designating the object designated by the return value of that operator; and if the designated object is actually a temporary, a dangling reference may result. This is a problem with such operators that offer implicit conversion of prvalues to lvalues, not a problem introduced by the conditional operator per se.

In both cases it is safe to bind a reference to the result, the usual rules for binding a reference to an lvalue or a prvalue apply. If the reference binds to a prvalue (either the prvalue result of the conditional, or a prvalue initialized from the lvalue result of the conditional), the lifetime of the prvalue is extended to match the lifetime of the reference.


In your original case, the conditional is:

true ? a : 2.

The second and third operand are: "lvalue of type float" and "prvalue of type double". This is case 5 in the cppreference summary, with the result being "prvalue of type double".

Then, your code initializes a const reference with a prvalue of a different (non-reference-related) type. The behaviour of this is to copy-initialize a temporary of the same type as the reference.

In summary, after const float & x = true ? a : 2.;, x is an lvalue denoting a float whose value is the result of converting a to double and back. (Not sure off the top of my head whether that is guaranteed to compare equal to a). x is not bound to a.


In bonus case 1, the second and third operand of the conditional operator are "lvalue of type float" and "lvalue of type const float". This is case 3 of the same cppreference link,

both are glvalues of the same value category and have the same type except for cv-qualification

The behavour is that the second operand is converted to "lvalue of type const float" (denoting the same object), and the result of the conditional is "lvalue of type const float" denoting the selected object.

Then you bind const float & to "lvalue of type const float", which binds directly.

So after const float & x = true ? a : b;, x is directly bound to either a or b.


In bonus case 2, true ? a_ref : 2. . The second and third operands are "lvalue of type const double" and "prvalue of type double", so the result is "prvalue of type double".

Then you bind this to const double & x, which is a direct binding since const double is reference-related to double.

So after const double & x = true ? a_ref : 2.; , then x is an lvalue denoting a double with the same value as a_ref (but x is not bound to a).

这篇关于在C ++中创建一个三元运算符结果的const引用是否安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆