通过 CRTP 的基础 class 引用访问派生 class 的 constexpr 成员变量
access a constexpr member variable of a derived class through a base class reference via CRTP
我 运行 在尝试通过 CRTP 的基础 class 引用访问派生 class 的 constexpr
成员变量时出错;
template <typename Der>
struct Base
{
constexpr std::size_t getsize()
{
constexpr const auto &b = static_cast<Der*>(this)->arr;
return b.size();
//return static_cast<Der*>(this)->arr.size(); // this works
}
};
struct Derived : Base<Derived>
{
static constexpr std::array<int, 10> arr = {};
};
int main(){
Derived d;
return d.getsize();
}
错误:
<source>:11:31: error: constexpr variable 'b' must be initialized by a constant expression
constexpr const auto &b = static_cast<Der*>(this)->arr;
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:24:14: note: in instantiation of member function 'Base<Derived>::getsize' requested here
return d.getsize();
^
<source>:11:53: note: use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function
constexpr const auto &b = static_cast<Der*>(this)->arr;
^
1 error generated.
Compiler returned: 1
更新
事实证明,删除参考作品中的 constexpr。我想了解为什么会这样?
auto &b = static_cast<Der*>(this)->arr;
return b.size();
通俗地说,您应该想象有一条规则,即函数内定义的 constexpr
变量 需要由表达式初始化 always 常量表达式,无论调用函数的情况如何。特别是,您总是可以这样做:
Base<Derived> b;
b.getSize();
在这种情况下 static_cast<Der*>(this)->arr
不是常量表达式,因为它实际上是 UB。因为像这样的事情可能性,你的函数根本无法编译,即使你可能永远不会以这种方式调用它。
您违反的 实际 规则是 [expr.const]/5.1。常量表达式 E 可能不会计算 this
,除非它是通过(直接或间接)调用其中发生 this
计算的某个成员函数。特别是,这意味着如果我们有这样的代码:
// namespace scope
struct S {
constexpr const S* get_self() {
const S* result = this;
return result;
}
};
constexpr S s;
constexpr const S* sp = s.get_self();
这里,s.get_self()
是一个常量表达式,因为对this
的访问只发生在get_self()
函数内部,这是对s.get_self()
求值的一部分。但是我们不能使 result
constexpr
,因为如果是这样,我们就不再需要“计算”封闭函数;初始化步骤本身必须符合常量表达式的条件,但事实并非如此,因为对 this
的访问现在是“裸”的。
对于您的代码,这意味着 getsize()
实际上可以 return 一个常量表达式(对于那些不触发 UB 的调用,如上所述)但是 b
不能 constexpr
.
我 运行 在尝试通过 CRTP 的基础 class 引用访问派生 class 的 constexpr
成员变量时出错;
template <typename Der>
struct Base
{
constexpr std::size_t getsize()
{
constexpr const auto &b = static_cast<Der*>(this)->arr;
return b.size();
//return static_cast<Der*>(this)->arr.size(); // this works
}
};
struct Derived : Base<Derived>
{
static constexpr std::array<int, 10> arr = {};
};
int main(){
Derived d;
return d.getsize();
}
错误:
<source>:11:31: error: constexpr variable 'b' must be initialized by a constant expression
constexpr const auto &b = static_cast<Der*>(this)->arr;
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:24:14: note: in instantiation of member function 'Base<Derived>::getsize' requested here
return d.getsize();
^
<source>:11:53: note: use of 'this' pointer is only allowed within the evaluation of a call to a 'constexpr' member function
constexpr const auto &b = static_cast<Der*>(this)->arr;
^
1 error generated.
Compiler returned: 1
更新 事实证明,删除参考作品中的 constexpr。我想了解为什么会这样?
auto &b = static_cast<Der*>(this)->arr;
return b.size();
通俗地说,您应该想象有一条规则,即函数内定义的 constexpr
变量 需要由表达式初始化 always 常量表达式,无论调用函数的情况如何。特别是,您总是可以这样做:
Base<Derived> b;
b.getSize();
在这种情况下 static_cast<Der*>(this)->arr
不是常量表达式,因为它实际上是 UB。因为像这样的事情可能性,你的函数根本无法编译,即使你可能永远不会以这种方式调用它。
您违反的 实际 规则是 [expr.const]/5.1。常量表达式 E 可能不会计算 this
,除非它是通过(直接或间接)调用其中发生 this
计算的某个成员函数。特别是,这意味着如果我们有这样的代码:
// namespace scope
struct S {
constexpr const S* get_self() {
const S* result = this;
return result;
}
};
constexpr S s;
constexpr const S* sp = s.get_self();
这里,s.get_self()
是一个常量表达式,因为对this
的访问只发生在get_self()
函数内部,这是对s.get_self()
求值的一部分。但是我们不能使 result
constexpr
,因为如果是这样,我们就不再需要“计算”封闭函数;初始化步骤本身必须符合常量表达式的条件,但事实并非如此,因为对 this
的访问现在是“裸”的。
对于您的代码,这意味着 getsize()
实际上可以 return 一个常量表达式(对于那些不触发 UB 的调用,如上所述)但是 b
不能 constexpr
.