получить доступ к переменной-члену 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.

Другие вопросы по тегам