7

我有以下测试代码:

#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 发生了一些可疑的事情。我猜想不编译的示例行实际上应该编译,这两个问题可能以某种方式相关。

第三点也是最后一点,对于那些概述我的代码的“诡计”或“不可读性”的人:可读性在很大程度上是一个主观问题,例如,我可以争辩说这样的速记可以使代码更有条理,这实际上可以提供帮助点缺陷。

4

3 回答 3

12

The second case:

return result = Result::INSUCCESS, nullptr;

looks like a gcc bug, it appears to be ignoring the left hand side of the comma operator in the context of a return statement, if I change the return statement to this (see it live):

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

there is no output but if we do this outside of a return statement we get the expected result. It appears to be fixed in 4.8 but I can reproduce with 4.6 and 4.7.

If we look at the draft C++ standard section 5.18 Comma operator it says (emphasis mine):

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.

Regardless, as several people have already mentioned this code is clever but hard to read and therefore will be hard to maintain, splitting this into two lines will work fine. I always like to keep in mind this quote from Brian Kernighan (there may be a few other versions of this):

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?

For the first case, the error is valid, if we look at section 4.10 Pointer conversions which says (emphasis mine going forward):

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.

the expression:

result = Result::INSUCCESS, NULL

is not a constant expression since it contains =, what is a constant expression is covered in section 5.19 Constant expressions and says:

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2) [...]

and includes:

an assignment or a compound assignment (5.17); or

Using nullptr is okay since it is a prvalue of std::nullptr_t, we can see that from section 12.14.7 Pointer literals which says:

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]

于 2014-05-09T12:30:06.727 回答
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

也许您的编译器中有错误?

于 2014-05-09T12:44:42.343 回答
-2

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

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

所以你得到'nullptr',它被转换为结果,也是成功的。

于 2014-05-09T12:04:03.270 回答