我有以下测试代码:
#include <cstdint> #include <cassert> enum class Result : std::uint32_t {SUCCESS = 0,INSUCCESS = 1}; void* func(Result& result) { // works great /* result = Result::INSUCCESS; return NULL; */ // error: invalid conversion from ‘long int’ to ‘void*’ [-fpermissive] /* return result = Result::INSUCCESS,NULL; */ // compiles,but <result> is not set??? return result = Result::INSUCCESS,nullptr; } void testReturnWithSideEffects() { Result result = Result::SUCCESS; func(result); assert(result == Result::INSUCCESS); }
这里有两个问题但我对第二个问题最感兴趣:
为什么没有设置结果?
编辑:
感谢大家的确认.我决定使用的解决方法是替换:
return result = Result::INSUCCESS,nullptr;
以下内容:
return result = Result::INSUCCESS,(void*)NULL;
附加说明:当然我的生产场景是另一种指针类型(不是void *),但为了说明的目的我进行了简化.
另一个注意事项:从解决方法中你可以看出,这个nullptr有一些可疑的东西.我猜测不编译的示例行应该实际编译,并且这两个问题可能以某种方式相关.
和第三和最后一点,那些谁介绍的“挂羊头卖狗肉”或者我的代码“不可读性”:可读性在很大程度上是一个主观的问题,比如我可以说,这样的简写可以使代码更有条理这实际上可以帮助现货缺陷.
解决方法
return result = Result::INSUCCESS,nullptr;
看起来像一个gcc bug,如果我将return语句更改为this(see it live),它似乎在return语句的上下文中忽略了逗号运算符的左侧:
return (printf("hello\n"),nullptr);
没有输出,但如果我们在return语句之外执行此操作,则会得到预期结果.它看起来固定在4.8但我可以用4.6和4.7重现.
如果我们看一下draft C++ standard部分5.18 Comma运算符,它说(强调我的):
A pair of expressions separated by a comma is evaluated left-to-right; the left expression is a discarded value expression (Clause 5).83 Every value computation and side effect associated with the left expression is sequenced before every value computation and side effect associated with the right expression. The type and value of the result are the type and value of the right operand; the result is of the same value category as its right operand,and is a bit-field if its right operand is a glvalue and a bit-field. If the value of the right operand is a temporary (12.2),the result is that temporary.
无论如何,正如几个人已经提到的那样,这段代码很聪明但难以阅读,因此难以维护,将其拆分为两行也能正常工作.我总是想记住这个quote from Brian Kernighan(可能还有其他几个版本):
Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it,how will you ever debug it?
对于第一种情况,如果我们查看4.10节指针转换(错过我的未来),则错误是有效的:
A null pointer constant is an integral constant expression (5.19)
prvalue of integer type that evaluates to zero or a prvalue of type
std::nullptr_t. A null pointer constant can be converted to a pointer
type; the result is the null pointer value of that type and is
distinguishable from every other value of object pointer or function
pointer type. Such a conversion is called a null pointer conversion.
表达方式:
result = Result::INSUCCESS,NULL
不是一个常量表达式,因为它包含=,5.19常量表达式中包含什么是常量表达式,并说:
A conditional-expression is a core constant expression unless it
involves one of the following as a potentially evaluated subexpression
(3.2) […]
包括:
an assignment or a compound assignment (5.17); or
使用nullptr是可以的,因为它是std :: nullptr_t的prvalue,我们可以从12.14.7节指针文字看到:
The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [ Note: std::nullptr_t is a distinct type that is neither a pointer type nor a pointer to member type; rather,a prvalue of this type is a null pointer constant and can be converted to a null pointer value or null member pointer value. See 4.10 and4.11. —endnote]