c++中的逻辑AND +赋值,安全吗?

Posted

技术标签:

【中文标题】c++中的逻辑AND +赋值,安全吗?【英文标题】:Logical AND + assignment in c++, safe? 【发布时间】:2011-09-26 12:18:37 【问题描述】:

我刚刚学到了这个很棒的模式(实际上是从 javascript 中学到的),我想将它应用到我的 c++ 代码中。

为了解释这个模式,假设我将一个字符串表示为这些的链表:

struct link_char;
struct link_char

   link_char * next;
   char code;
;

请注意,任何 link_char 字符串的最后一个字符将始终具有 code==0。 这个属性意味着我可以检查字符串中的值,同时使用 && 短路来防止 NULL 指针访问。

bool equals_hello( const link_char * first_char )

    const link_char * c = first_char;

    return       c->code=='h' 
    && (c=c->next)->code=='e' 
    && (c=c->next)->code=='l' 
    && (c=c->next)->code=='l' // if string == "hel", we short-circuit here
    && (c=c->next)->code=='o';

我的问题是关于安全性,而不是可读性。 我知道只要 && 没有过载,短路就会起作用。但是分配操作会以正确的顺序发生,还是由实现定义?

上面的例子清楚地说明了读/写可能发生在哪里,但我也想在可能有副作用的情况下使用这种模式。例如:

// think of these as a bunch of HRESULT type functions 
//   a return value of 0 means SUCCESS
//   a return value of non-zero yields an Error Message
int err;
( !(err=initialize()) && !(err=create_window()) && !(err=run_app() )
    || handle_error(err);

这些类型的操作会按预期跨平台工作吗?我读过“如果你在一个表达式中读取一个变量两次,你也写它,结果是未定义的”。但直觉上我觉得短路保证了顺序,不是吗?

【问题讨论】:

我假设没关系,因为&& 是一个序列点。但我可能弄错了。 回复:您对可读性的评论;我不会签入这样的代码,除非我想用指向这个问题的链接来评论它的每个实例:) 另外,您的第一个示例在取消引用 code 之前不会检查 NULL 恕我直言,我不会推荐这种编码风格。所以我不在乎它是否有效。 链接条件和捕获错误代码并非不合理,但更容易阅读if ((err = initialize()) || (err = create_window()) || (err == run_app())) handle_error(err); 【参考方案1】:

是的。

内置逻辑与 (&&)、逻辑或 (||) 和逗号运算符 (,) 是 的情况,对于二元运算符,C++ 保证评估将计算左表达式,然后(如果不是短路)右表达式(逗号运算符当然总是计算两个操作数,先左后右)。

还请注意,函数参数之间的逗号不是逗号运算符,因此未指定函数参数的评估顺序,甚至更糟:例如在f(g(h()),i()) 中,调用顺序可能是h,i,g,f.

此外,关于评估顺序的保证仅适用于内置运算符;如果你重新定义它们,那么它们基本上变成了函数调用,其中参数的评估顺序无法保证并且不会执行短路。

其他二元运算符不保证评估的顺序,例如一个常见的错误是认为:

std::cout << foo() << bar();

foo() 的调用肯定会在对bar() 的调用之前发生……这是不正确的。

当然,三元 :? 运算符也保证了计算顺序,在第一次计算条件后,只会计算其他两个表达式中的一个。

另一个保证求值顺序的地方(有时令新手感到惊讶)是构造函数的成员初始化列表,但在这种情况下,顺序不是表达式中的顺序,而是类中成员声明的顺序....例如:

struct Foo

   int x, y;
   Foo() : y(compute_y()), x(compute_x()) 
;

在这种情况下,保证调用 compute_x() 将在调用 compute_y() 之前完成,因为在成员声明中xy 之前。

【讨论】:

+1 表示关于流运算符的观点。确实非常违反直觉。 我认为在运算符重载的情况下不能保证(我的意思是在运算符重载的情况下&&,...)。这就是为什么建议不要重载这些运算符的原因。只是不要这样做。 @Tobias:这已经在问题中说明了,但我还是添加了说明。谢谢。 我只是想为阅读您精彩文章的读者添加此建议。【参考方案2】:

这些类型的操作会作为 打算跨平台?我读了 那“如果你在 一个你也写它的表达式, 结果是未定义的”。但是 直觉上我觉得 短路保证顺序, 不是吗?

内置的&amp;&amp; 运算符保证了短路求值,这意味着它引入了一个序列点:C++98 §5.14/2“除临时对象的破坏外,第一个表达式的所有副作用 (12.2 ) 发生在计算第二个表达式之前”。

所以没有问题。 C++。

在我看来,您建议的用法仍然很不好,因为它是晦涩难懂的。不要使用您必须询问的语言功能,因为其他人很可能同样不清楚它们。另外,请注意代码中的重新设置,请注意 Windows HRESULT 在设置第 31 位时指示失败,这与零/非零非常不同。

干杯,

【讨论】:

以上是关于c++中的逻辑AND +赋值,安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 隐式生成的赋值运算符的异常安全性

为啥 ~= 在 C++ 中缺少唯一的非逻辑赋值运算符? [关闭]

临时人员的 C++ 生命周期 - 这安全吗?

C++ - 让派生类从基类“继承”重载赋值运算符的安全/标准方法

dotnet C# 给结构体字段赋值非线程安全

dotnet C# 给结构体字段赋值非线程安全