You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++20中模板内decltype作用于立即调用lambda是否符合标准规范?

我在尝试实现一个小型DSL时,遇到了一个clang和gcc都没有按照预期处理的奇怪语法构造:

#include <utility>

template <class...>
struct disjunction {};

template <auto>
struct literal {};

template <class T, T Min, T Max>
using range = decltype([]<T... Cs>(std::integer_sequence<T, Cs...>) {
    return disjunction<literal<T{Min + Cs}>,..., literal<Max>>{};
}(std::make_integer_sequence<T, Max - Min>{}));

template <class T, T... Cs> requires (sizeof...(Cs) == 1)
constexpr literal<Cs...> operator""_c() { return {}; }

template <class T, T Min, T Max>
constexpr range<T, Min, Max> operator-(literal<Min>, literal<Max>) { return {}; }

constexpr auto digits = "0"_c - "9"_c;

using expected = const disjunction<
    literal<'0'>, literal<'1'>, literal<'2'>, literal<'3'>, literal<'4'>,
    literal<'5'>, literal<'6'>, literal<'7'>, literal<'8'>, literal<'9'>>;

static_assert(std::is_same_v<decltype(digits), expected>);

该示例中,clang直接崩溃,gcc则将digits推导为值为0的int类型。

我找到了一种使用模板偏特化的替代实现方案:

namespace detail {
template <class, auto, auto> struct range;

template <class T, T... Cs, T Min, T Max>
struct range<std::integer_sequence<T, Cs...>, Min, Max> {
    using type = disjunction<literal<T{Min + Cs}>,..., literal<Max>>;
};
} // namespace detail

template <class T, T Min, T Max>
using range = typename detail::range<
    std::make_integer_sequence<T, Max - Min>, Min, Max>::type;

该实现下上述static_assert在clang和gcc上都能通过,但我希望不需要借助类模板辅助就能实现该功能。

另外,使用最初的range实现时,如果将"0"_c - "9"_c表达式包裹在decltype(...)中:

using digits = decltype("0"_c - "9"_c);

using expected = disjunction<
    literal<'0'>, literal<'1'>, literal<'2'>, literal<'3'>, literal<'4'>,
    literal<'5'>, literal<'6'>, literal<'7'>, literal<'8'>, literal<'9'>>;

static_assert(std::is_same_v<digits, expected>);

此时clang不会崩溃,也不会对static_assert报诊断信息,但gcc仍然编译失败。如果通过如下代码检查digits的实际类型:

template <class Type>
auto f() { static_assert(sizeof(Type) == 0); }

int main() { f<digits>(); }

clang会再次崩溃并输出错误:

error: cannot compile this l-value expression yet
int main() { f<digits>(); }
             ^~~~~~~~~

这个错误信息似乎暗示clang认为该语法构造是合规的,但我无法确认。我想咨询,我使用decltype作用于立即调用lambda来实现range模板的写法,在C++20标准中是否是良构的?


回答

你使用decltype作用于立即调用lambda实现range别名模板的写法在C++20标准中是良构的,你遇到的所有异常表现都是Clang和GCC的实现缺陷导致的。

依据说明

  • C++20明确允许lambda表达式出现在decltype这类不求值语境中,没有任何标准规则禁止这种用法。
  • 代码中用到的模板lambda、模板参数包展开、字符字面量运算符等特性均为C++20的合法特性,组合使用没有违反任何标准约束。
  • 你观察到的Clang内部崩溃(ICE)、GCC错误推导为int类型的问题,都是编译器对C++20新特性的支持不完善导致的,和代码本身的合法性无关。

兼容建议

目前最新版本的Clang和GCC仍未修复该类bug,如果你需要保证跨编译器的兼容性,建议暂时使用你已经实现的类模板偏特化辅助方案,该方案是目前稳定性最好的实现。


内容的提问来源于stack exchange,提问作者Patrick Roberts

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 22:36:03