c++ - 当 return 语句、逗号运算符、花括号初始化列表和 std::unique_ptr 组合在一起时

标签 c++

在下面的代码中,为什么我在 n = 8 时出现编译错误,而所有其他情况都正常?我的目的是报告一些错误并返回空指针,而不用不必要的大括号使代码困惑。我将使用 nullptr 来实现此目的,但我很好奇为什么 {} 无法使用逗号运算符进行编译,而它单独工作。

您可以使用此代码 here 。我使用了 C++20 设置。

#include <cstdint>
#include <memory>

void oops(const char*) {
}

std::unique_ptr<uint8_t[]> please_do(int n) {
    if (n == 1)
        return std::unique_ptr<uint8_t[]>(); // compiles ok
    if (n == 2)
        return oops(""), std::unique_ptr<uint8_t[]>(); // compiles ok

    if (n == 3)
        return std::make_unique<uint8_t[]>(n); // compiles ok
    if (n == 4)
        return oops(""), std::make_unique<uint8_t[]>(n); // compiles ok

    if (n == 5)
        return nullptr; // compiles ok
    if (n == 6)
        return oops(""), nullptr; // compiles ok

    if (n == 7)
        return {}; // compiles ok
    if (n == 8)
        return oops(""), {}; // why compilation error?

    return nullptr; // compiles ok
}

int main() {
    please_do(42);
    return 0;
}

GCC 9.2 输出:

<source>: In function 'std::unique_ptr<unsigned char []> please_do(int)':

<source>:26:26: error: expected primary-expression before '{' token

   26 |         return oops(""), {}; // why compilation error?

      |                          ^

<source>:26:25: error: expected ';' before '{' token

   26 |         return oops(""), {}; // why compilation error?

      |                         ^~

      |                         ;

Clang 9.0.0 输出:

<source>:26:16: error: no viable conversion from returned value of type 'void' to function return type 'std::unique_ptr<uint8_t []>'

        return oops(""), {}; // why compilation error?

               ^~~~~~~~

/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:528:7: note: candidate constructor not viable: cannot convert argument of incomplete type 'void' to 'std::unique_ptr<unsigned char [], std::default_delete<unsigned char []> > &&' for 1st argument

      unique_ptr(unique_ptr&& __u) noexcept

      ^

/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:533:12: note: candidate constructor template not viable: cannot convert argument of incomplete type 'void' to 'std::nullptr_t' (aka 'nullptr_t') for 1st argument

        constexpr unique_ptr(nullptr_t) noexcept

                  ^

/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:681:7: note: candidate constructor not viable: cannot convert argument of incomplete type 'void' to 'const std::unique_ptr<unsigned char [], std::default_delete<unsigned char []> > &' for 1st argument

      unique_ptr(const unique_ptr&) = delete;

      ^

/opt/compiler-explorer/gcc-9.2.0/lib/gcc/x86_64-linux-gnu/9.2.0/../../../../include/c++/9.2.0/bits/unique_ptr.h:542:2: note: candidate template ignored: could not match 'unique_ptr<type-parameter-0-0, type-parameter-0-1>' against 'void'

        unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept

        ^

<source>:26:24: error: expected expression

        return oops(""), {}; // why compilation error?

                       ^

最佳答案

{} 不是表达式。 return {...}; 是它自己的特殊语法( list initialization 的形式),它从函数的签名中计算出返回类型,就像通过 Ret{...} 其中 Ret 是返回类型:

Ret f() {
    return {...}; // special syntax for return; {args...} need not be a valid expression
}
// same as
Ret f() {
    return Ret{...}; // generic return <expression> syntax; Ret{args...} must be a valid expression
}

return a, b 中没有特殊语法。它只是使用正常 return 语法返回表达式 a, b。逗号运算符要求 ab 都是表达式,但由于 {} 不是表达式,因此表达式与 oops("") 类似,{} 是无效语法。

顺便说一句,还有其他地方可以使用 {...} ,使其看起来像是一个表达式,但实际上不是,例如函数调用:

void f(std::string);
f({5, 'a'})

同样,虽然这看起来像 {5, 'a'} 是一个类型为 std::string 的表达式(正如我们的意图),但事实并非如此。 {5, 'a'} 是函数调用本身的一部分,其类型由重载解析决定。

至于编译器错误,它们都因 oops(""), {} 作为表达式的语法无效而感到困惑。 GCC 似乎已读取 oops(""),,因此期望后面有一个表达式。但是表达式不能以 { 开头,因此它读取该字符并立即呕吐,提示它期望表达式开始。 Clang 也做同样的事情。我认为 Clang 然后会继续追求“有帮助”,假装您只是没有编写 , {}; 部分(也就是说,就好像您编写了 return oops("");),并为此发出错误。这种行为例如是什么让 Clang 能够提供有用的类型错误,即使你拼写错误;也就是说,如果您要运行 Clang

void supercalifragilisticexpialidocious(std::string);
int main() { supercalifragilisticexpialidociuos(7); }

它首先会因拼写错误 (-ous -> -uos) 发出错误,建议正确的拼写,然后会因 之间的类型不匹配发出错误int 7 和参数类型 std::string 就好像您已经修复了拼写一样。同样,在这里,它会因错误语法发出一个错误,然后发出第二个错误,就好像您已经摆脱了错误语法一样。但是,在这种情况下它并不是很有帮助,因为它想象您使用的“修复”并不是实际的修复。

关于c++ - 当 return 语句、逗号运算符、花括号初始化列表和 std::unique_ptr 组合在一起时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59473651/

相关文章:

C++ Push_back vector<struct>MyVector 和错误 C2664

c++ - 为什么 g++ 编译的代码会超出堆栈指针的范围?

c++ - 使用 CScope 查找函数调用(非定义)(C/C++)

c++ - 为什么 'std::endl' 在语句 'std::cout << std::endl;"中使用时需要命名空间限定,给定参数相关查找?

c++ - 无法增加 QToolButton 的大小(按钮样式设置为 Qt::ToolButtonTextBesideIcon)

c++ - 如何使用 QtCreator 将内部库添加到构建中

C++ uint64 和 *uint64 有什么区别?

c# - 如何放置 90X90 图标

c++ - 我可以将 C++ 代码(或用 C++ 编写的库)与混合移动应用程序代码混合使用吗?

c# - 在 Windows Mobile 应用程序中播放 YouTube 视频