逗号运算符,return语句和nullptr没有副作用?

hae*_*lix 7 c++ gcc comma nullptr c++11

我有以下测试代码:

#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);
}
Run Code Online (Sandbox Code Playgroud)

这里有两个问题但我对第二个问题最感兴趣:

为什么没有设置结果?

编辑:感谢大家的确认.我决定使用的解决方法是替换:

return result = Result::INSUCCESS, nullptr;
Run Code Online (Sandbox Code Playgroud)

以下内容:

return result = Result::INSUCCESS, (void*)NULL;
Run Code Online (Sandbox Code Playgroud)

附加说明:当然我的生产场景是使用另一种指针类型(不是void*),但为了说明的目的我进行了简化.

另一个注意事项:从解决方法中你可以看出,这个nullptr有一些可疑的东西.我猜测不编译的示例行应该实际编译,并且这两个问题可能以某种方式相关.

对于那些概述我的代码的"欺骗性"或"不可读性"的人来说,第三个也是最后一个注意事项:可读性在很大程度上是一个主观问题,例如我可以说这样的速记可以使代码更加结构化,这实际上可以帮助现货缺陷.

Sha*_*our 12

第二种情况:

return result = Result::INSUCCESS, nullptr;
Run Code Online (Sandbox Code Playgroud)

看起来像一个gcc bug,它似乎忽略了一个return语句上下文中逗号运算符的左侧,如果我将return语句更改为this(请参见实时):

return (printf("hello\n"), nullptr);
Run Code Online (Sandbox Code Playgroud)

没有输出,但如果我们在return声明之外这样做,我们得到预期的结果.它似乎是固定的,4.8但我可以用4.6和重现4.7.

如果我们看一下C++标准部分的5.18 逗号运算符它(强调我的):

用逗号分隔的一对表达式从左到右进行评估; 左表达式是废弃的值表达式(第5条).83 在与右表达式相关的每个值计算和副作用之前,对与左表达式相关联的每个值计算和副作用进行排序.结果的类型和值是右操作数的类型和值; 结果与右操作数具有相同的值类别,如果右操作数是glvalue和位域,则是一个位域.如果右操作数的值是临时值(12.2),则结果是临时值.

无论如何,正如几个人已经提到的那样,这段代码很聪明但难以阅读,因此很难维护,将其分成两行就行了.我总是想记住Brian Kernighan的句话(可能还有其他一些版本):

每个人都知道调试的难度是首先编写程序的两倍.因此,如果你在编写它时就像你一样聪明,你将如何调试它?

对于第一种情况,如果我们看一下4.10 Pointer转换部分(强调我的未来),那么错误是有效的:

空指针常量是整数类型的整数常量表达式(5.19)prvalue,其计算结果为零 类型为std :: nullptr_t的prvalue.空指针常量可以转换为指针类型; 结果是该类型的空指针值,并且可以与对象指针或函数指针类型的每个其他值区分开.这种转换称为空指针转换.

表达方式:

result = Result::INSUCCESS, NULL
Run Code Online (Sandbox Code Playgroud)

不是一个常量表达式,因为它包含=,常量表达式涵盖了什么是5.19 常量表达式,并说:

条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式(3.2)[...]

包括:

作业或复合作业(5.17); 要么

使用nullptr是可以的,因为它是std :: nullptr_t的prvalue ,我们可以从12.14.7 Pointer文字部分看到:

指针文字是关键字nullptr.它是std :: nullptr_t类型的prvalue.[注意:std :: nullptr_t是一个不同的类型,既不是指针类型也不是指向成员类型的指针; 相反,这种类型的prvalue是一个空指针常量,可以转换为空指针值或null成员指针值.见4.10和4.11.-endnote]