非类型模板参数的无效类型转换

Invalid type conversion of non-type template argument

提问人:303 提问时间:11/8/2023 更新时间:11/9/2023 访问量:140

问:

我希望它可以用作类型的转换常量表达式,因为用户定义的转换运算符。但是,GCC 和 MSVC 拒绝此代码,而 Clang 似乎接受它。根据 C++ 标准,代码应该是合法的吗?xboolconstexpr operator bool() const

template<bool B>
struct s { constexpr operator bool() const { return B; } };
constexpr auto f(auto x) -> s<x> { return {}; }
static_assert(f(s<true>{})); // clang ok, gcc nope, msvc nope

现场示例


来自 GCC 的错误消息:

<source>:3:32: error: template argument 1 is invalid
    3 | constexpr auto f(auto x) -> s<x> { return {}; }
      |                                ^
<source>:3:32: error: template argument 1 is invalid
<source>:3:32: error: template argument 1 is invalid
<source>:3:32: error: template argument 1 is invalid
<source>:3:29: error: invalid template-id
    3 | constexpr auto f(auto x) -> s<x> { return {}; }
      |                             ^
<source>:3:32: error: use of parameter outside function body before '>' token
    3 | constexpr auto f(auto x) -> s<x> { return {}; }
      |                                ^
<source>:3:11: error: deduced class type 's' in function return type
    3 | constexpr auto f(auto x) -> s<x> { return {}; }
      |           ^~~~
<source>:2:8: note: 'template<bool B> struct s' declared here
    2 | struct s { constexpr operator bool() const { return B; } };
      |        ^
<source>:4:15: error: 'f' was not declared in this scope
    4 | static_assert(f(s<true>{}));
      |               ^

来自 MSVC 的错误消息:

<source>(4): error C2440: 'static_assert': cannot convert from 's<x>' to 'bool'
<source>(4): note: No user-defined-conversion operator available that can perform
this conversion, or the operator cannot be called
C++ 语言-律师 C++20

评论

1赞 Ted Lyngmo 11/8/2023
删除尾随返回类型,并在函数内部执行。return s<x>{};
0赞 303 11/8/2023
@TedLyngmo 这是一种不应掉以轻心的解决方法,因为它的成本并不总是可以接受的:youtube.com/watch?v=Tnl7FnwJ2Uw&t=2583s
0赞 Ted Lyngmo 11/8/2023
你是说清晰度的代价吗?好吧,使用推断的返回类型,其中类型很明显,这并不是让它工作的成本很高。在这种情况下,它非常明显,因此不会损失太多清晰度。在非常复杂的函数中,我有时会对感兴趣的类型进行 typedef。函数中的第一行可能是明确这一点。using return_type = s<x>;

答:

2赞 ecatmur 11/8/2023 #1

根据 GCC 维护者的评论,gcc 的动机是通过对 [basic.scope.param] 的注释来拒绝的:

[注 1:函数参数不能用于 parameter-declaration-clause ([dcl.fct.default]) 中的值。— 结束语]

但是,您没有将参数用于其值(转换运算符不访问类对象),因此此注释无关紧要(当然,也是非规范的)。没有其他拒绝的理由,因此根据 [expr.const]/5,应该接受代码。

如果将声明更改为:f

constexpr auto f(auto x) -> decltype([](auto y) { return s<y>(); }(x)) { return {}; }

然后 gcc 和 MSVC 接受,但 clang 拒绝。

评论

0赞 303 12/9/2023
标准中的哪个特定文本允许使用 ?感兴趣的部分是调用成员函数的地方,而不需要实际的对象来操作。[](auto x) -> decltype(+std::get<x>(std::array{0})) { return {}; }(std::bool_constant<true>{});xconstexpr operator bool() conststd::bool_constant
0赞 ecatmur 12/13/2023
@303 expr.const/5:“表达式 E 是核心常量表达式,除非 [...]”和示例 5 到 expr.const/8。