与逗号操作符、返回语句和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);
}

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

为什么没有设置结果?

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

return result = Result::INSUCCESS, nullptr;

与以下内容:

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

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

另一个注意事项:从解决方案中可以看出,nullptr中有一些可疑的东西。我猜这个不能编译的示例行实际上应该可以编译,这两件事可能在某种程度上是相关的。

第三点也是最后一点,对于那些概述我的代码的"诡计"或"不可读性"的人:可读性很大程度上是一个主观问题,例如,我可以认为这样的简写可以使代码更结构化,实际上可以帮助发现缺陷。

第二种情况:

return result = Result::INSUCCESS, nullptr;

看起来像一个gcc错误,它似乎忽略了返回语句上下文中的逗号操作符的左侧,如果我将return语句更改为以下(查看它的实时):

return (printf("hellon"), nullptr);

没有输出,但如果我们在return语句之外执行此操作,我们将得到预期的结果。它似乎是固定在4.8,但我可以复制4.64.7

如果我们看一下c++标准草案5.18 逗号操作符它说(强调我的):

用逗号分隔的一对表达式从左到右求值;83与左表达式相关的每个值计算和副作用在与右表达式相关的每个值计算和副作用之前进行排序。结果的类型和值就是右操作数的类型和值;结果与右操作数的值类别相同,如果右操作数是左值和位域,则结果为位域。如果右操作数的值是临时类型(12.2),则结果为该临时类型。

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

每个人都知道调试是编写程序的两倍难。因此,如果您在编写它时尽可能聪明,那么您将如何调试它呢?

对于第一种情况,错误是有效的,如果我们看一看4.10 指针转换,它说(强调我向前):

空指针常量是整型常量表达式(5.19)类型为零的整型右值std:: nullptr_t 。空指针常量可以转换为指针类型;结果是该类型的空指针值区别于其他对象、指针或函数的值指针类型。这样的转换称为空指针转换。

表达式:

result = Result::INSUCCESS, NULL

不是一个常量表达式,因为它包含=,什么是常量表达式在5.19 章节中介绍常量表达式,并说:

条件表达式是核心常量表达式,除非它包含以下一个可能求值的子表达式(3.2)[…]

,包括:

赋值或复合赋值(5.17);或者

使用nullptr是可以的,因为它是std::nullptr_t的右值,我们可以从12.14.7指针字面量看到:

指针字面量是关键字nullptr。类型为std:: nullptr_t 。[注:std::nullptr_t是一个不同的类型。既不是指针类型,也不是指向成员类型的指针;更确切地说,是一个右值此类型的是空指针常量,可以转换为空值指针值或空成员指针值。参见4.10和4.11。尾注)

我的测试表明您的代码应该可以工作。我在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

也许你的编译器有bug ?

引用c++标准-逗号操作符:

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

的类型和值。

所以你得到'nullptr',它被转换成结果,也是SUCCESS

相关内容

  • 没有找到相关文章

最新更新