首页 > 解决方案 > 可以在 C++20 中的 `decltype` 或 `sizeof` 表达式中定义类型吗?

问题描述

由于 C++20 lambda 函数允许在未计算的上下文中使用,特别是它们应该被允许在decltypesizeof表达式中使用。

反过来,lambda 可以在它们的主体中定义一些类型,并可能返回这些类型的对象,例如:

using T = decltype( []{ struct S{}; return S{}; } );

[[maybe_unused]] constexpr auto N 
          = sizeof( []{ struct S{}; return S{}; } );

Clang 接受此代码,但 GCC 发出错误:

error: types may not be defined in 'decltype' expressions
    1 | using T = decltype( []{ struct S{}; return S{}; } );
error: types may not be defined in 'sizeof' expressions
    4 |           = sizeof( []{ struct S{}; return S{}; } );

演示:https ://gcc.godbolt.org/z/9aY1KWfbq

哪一个编译器在这里?

标签: c++lambdalanguage-lawyerc++20decltype

解决方案


在P0315R4中删除的未评估上下文中对 lambdas 的限制意味着这是不被禁止的(尽管是极端情况),因此可以说 GCC 在这里有一个错误。特别是本文中的以下讨论与此处相关:

此外,在核心反射器上提出了一些关于重新声明的问题,如下所示:

template <int N> static void k(decltype([]{ return 0; }()));
template <int N> static void k(decltype([]{ return 0; }())); // okay
template <int N> static void k(int); // okay

这些应该是有效的重新声明,因为 lambda 表达式是经过计算的,它们的主体中既不包含模板参数,也不是包含模板参数的完整表达式的一部分。因此,lambda-expression 不需要出现在函数的签名中,其行为与此等价,无需任何特殊措辞:

struct lambda { auto operator()() const { return 0; } };
template <int N> static void k(decltype(lambda{}()));
template <int N> static void k(decltype(lambda{}())); // okay today
template <int N> static void k(int); // okay today

同样的论点也适用于 OP 的示例,并且(可以说)“行为与此等效,不需要任何特殊措辞”

struct lambda { auto operator()() const { struct S{}; return S{}; } };
using T = decltype(lambda{});

[[maybe_unused]] constexpr auto N 
          = sizeof( []{ struct S{}; return S{}; } );

请注意,这sizeof更简单,因为您正在查询无捕获 lambda 的闭包类型的大小,而不是本地 struct 的大小S


推荐阅读