получить доступ к переменной-члену constexpr производного класса через ссылку базового класса через CRTP
Я сталкиваюсь с ошибкой при попытке доступа к
constexpr
переменная-член производного класса через ссылку базового класса через CRTP;
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();
1 ответ
Неформально вы должны представить себе, что существует правило, согласно которому переменная , определенная внутри функции, должна быть инициализирована выражением, которое всегда является константным выражением, независимо от обстоятельств, при которых вызывается функция. В частности, вы всегда можете сделать что-то вроде этого:
Base<Derived> b;
b.getSize();
в таком случае
static_cast<Der*>(this)->arr
не является постоянным выражением, так как на самом деле это UB. Из-за возможности подобных вещей ваша функция вообще не может скомпилироваться, даже если вы все равно никогда не вызовете ее таким образом.
На самом деле вы нарушаете правило [expr.const]/5.1. Константное выражение E не может оценивать , если только оно не вызвано (прямо или косвенно) некоторой функцией-членом, внутри которой происходит вычисление. В частности, это означает, что если у нас есть такой код:
// 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();
Здесь постоянное выражение, так как доступ к происходит только внутри
get_self()
функция, которая является частью оценки
s.get_self()
. Но мы не можем сделать
result
, потому что если бы это было так, мы больше не могли бы «подсчитывать» объемлющую функцию; шаг инициализации должен был бы сам по себе квалифицироваться как константное выражение, которым он не является, поскольку доступ к
this
сейчас "голый".
Для вашего кода это означает, что
getsize()
на самом деле может возвращать постоянное выражение (для тех вызовов, которые не вызывают UB, как описано выше), но
b
не может быть сделано
constexpr
.