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
相关产品推荐
相关产品推荐

