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

时间:2014-05-09 11:57:01

标签: c++ gcc c++11 comma nullptr

我有以下测试代码:

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

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

为什么没有设置结果?

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

return result = Result::INSUCCESS, nullptr;

以下内容:

return result = Result::INSUCCESS, (void*)NULL;

补充说明:当然我的制作方案是使用另一种指针类型(不是void *),但为了说明的目的我进行了简化。

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

还有第三个也是最后一个注释,对于那些勾勒出&#34;技巧&#34;或者&#34;不可读性&#34;我的代码:可读性在很大程度上是一个主观问题,例如我可以说这样的简写可以使代码更加结构化,实际上可以帮助发现缺陷。

3 个答案:

答案 0 :(得分:12)

第二种情况:

return result = Result::INSUCCESS, nullptr;

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

return (printf("hello\n"), nullptr);

没有输出,但如果我们在return语句之外执行此操作,则会获得预期结果。它似乎已在4.8中修复,但我可以使用4.64.7进行复制。

如果我们查看draft C++ standard部分5.18 逗号运算符,它会说(强调我的):

  

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

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

  

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

对于第一种情况,如果我们查看4.10 指针转换部分(强调我的前进),则错误有效:

  

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

表达式:

result = Result::INSUCCESS, NULL

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

  

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

并包括:

  

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

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

  

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

答案 1 :(得分:0)

我的测试表明您的代码应该有效。我在compileonline.com上运行了这个:

#include <iostream>

using namespace std;

int func5() {
    return 5;
}

int func(int& result) {
    return result = func5(), 10;
}

enum class Result : 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;
}

int main()
{
    int b = 0;
    int a = func(b);
    cout << a << ", " << b << endl;

    Result result = Result::SUCCESS;
    func(result);
    cout << uint32_t(result) << endl;

}

结果:

Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1

Executing the program....
$demo 
10, 5
1

也许你的编译器有错误?

答案 2 :(得分:-2)

引用C ++标准 - 逗号运算符:

  

结果的类型和值是右操作数的类型和值

所以你得到了#null; nullptr&#39;,它被转换为结果,也是SUCCESS。