我正在尝试使用
int
对象的 constexpr
成员作为模板参数。我的想法是,我创建一个 constexpr Symbol
,然后将其转换为 SymbolRef<1>
(在实际代码中,1 对于不同的对象是不同的)。
template<int Id>
struct SymbolRef { };
struct Symbol {
int const id_;
consteval Symbol() : id_(1) { }
template<int Id>
consteval operator SymbolRef<Id>() const
{
return SymbolRef<id_>{};
}
};
但是,尝试编译上述代码时出现错误:
error: non-type template argument is not a constant expression
12 | return SymbolRef<id_>{};
| ^~~
<source>:12:22: note: implicit use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function
短语“仅允许在调用‘constexpr’成员函数的评估中使用”似乎不适用:这是一个
consteval
函数!?
我知道,当
operator SymbolRef<Id>()
是 constexpr
成员函数时(尽管有错误消息!),此代码格式不正确,因为在这种情况下,该函数还必须在没有 constexpr
的情况下进行编译,在这种情况下 this->id_
不能用作模板参数。
为什么这段代码与
consteval
一起使用时是非法的?
编辑:
以下确实使用g++进行编译 - 但仅在使用
this->id_
时。
template<int Id>
struct SymbolRef { };
struct Symbol {
int const id_;
consteval Symbol() : id_(1) { }
template<int Id>
consteval operator SymbolRef<Id>() const
{
return SymbolRef<this->id_>{};
}
};
我猜这是一个编译器错误?看来正确的行为是无论有没有
this->
都可以编译,而 clang++ 是错误的。
consteval
/constexpr
与此处无关。
非类型模板参数,无论出现在何处,其本身都必须是常量表达式。
您引用的规则是不完整的。 [expr.const]/5.1 说:
模板参数中的表达式 E 是核心常量表达式,除非对 E 的求值遵循抽象机 ([intro.execution]) 的规则,将求值以下其中一项:
- [...]
,除了作为 E 的一部分进行计算的 constexpr 函数;this
- [...]
id_
隐式解释为 this->id_
,因为 id_
命名了类的成员。这个 this
确实出现在 constexpr 函数内部,但不是在考虑的常量表达式求值期间调用的 constexpr 函数。
要了解为什么这不起作用,请考虑编译器必须在实例化模板时确定每个表达式的类型,然后才能知道可能调用函数的
id_
的值。每次调用相同的函数特化时,类型是否应该不同?这在静态类型语言中是不可能的。