Как C++ обрабатывает оценку constexpr для указателей нестатических функций-членов на объектах времени выполнения?

Код компилируется и запускается, но я пытаюсь понять, как выражение (obj.*funcPtr)(12) можно оценить во время компиляции, когда obj не объявлено как constexpr. Я ожидаю, что это может не соответствовать стандартным требованиям для оценки во время компиляции.

#include <iostream>
#include <stdexcept>

template <typename T, size_t N>
class Array
{
public:

    T& operator[](size_t i)
    {
        if (i>=N)
        {
            throw std::out_of_range("Bd index!");
        }
        return data_[i];
    }
    constexpr size_t Size() const
    {
        return N;
    }

private:
    T data_[N]{};
};



class MyClass
{
public:
   constexpr int myFunction(int value)
    {
        return value*2;
    }
};

int main()
{

   constexpr int (MyClass::*funcPtr)(int) = &MyClass::myFunction;
   MyClass obj;
   Array<int ,  (obj.*funcPtr)(12) > arr;
   std::cout << arr.Size() << '\n';

}

Вы можете сократить свою программу. См. уменьшенный пример

user12002570 21.07.2024 18:40

Это работает, потому что myFunction не зависит ни от одного члена MyClass obj. Если вы добавите зависимость от такого члена, он не сможет скомпилироваться так, как вы ожидали.

wohlstad 21.07.2024 18:40
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вызов функции является постоянным выражением, поскольку ни один из пунктов [expr.cont]/5 не лишает ее права быть постоянным выражением. В частности, вы не пытаетесь выполнить какое-либо проблемное преобразование lvalue в rvalue в значении пункта 5.9. Единственное преобразование lvalue в rvalue, которое вы выполняете, — это value, срок жизни которого начался во время вычисления постоянного выражения, и funcPtr, который явно отмечен constexpr.

С точки зрения реализации: ваша функция myFunction не имеет доступа ни к одному элементу данных obj. Таким образом, компилятор может определить свой результат во время компиляции по заданному аргументу, и ему вообще не нужно заботиться о реальном экземпляре obj. Его состояние не влияет на результат вызова функции. Результат всегда 24.

если вы измените obj на указатель, gcc/clang больше не будет считать вызов через указатель constexpr, хотя с точки зрения реализации это то же самое (не уверен насчет стандарта). MSVC не согласился и скомпилировал его. Интересно, какой из них правильный? См.: godbolt.org/z/c1zx1cadE

Gene 21.07.2024 19:24

@Gene Разыменование нулевого указателя (часть *obj) является явно неопределенным поведением, а неопределенное поведение лишает выражение права быть (основным) постоянным выражением для каждого элемента [expr.const]/5.8. То, что разыменование указателя само по себе вызывает UB, даже если результат не используется, было разъяснено относительно недавно в CWG 2823, хотя вызов нестатической функции-члена для нулевого указателя всегда был UB. MSVC ошибается.

user17732522 21.07.2024 20:12

@Gene Кроме того, в этом случае необходимо знать фактическое значение указателя, т. е. удаление constexpr из указателя не сработает, поскольку *obj требует преобразования lvalue в rvalue на obj. (Встроенный * можно применять только к значениям prvalues.)

user17732522 21.07.2024 20:14

@Gene Конечно, с точки зрения реализации это в любом случае не было бы проблемой, но здесь стандарт принял явное решение считать конструкцию неопределенной (либо потому, что семантически это невозможно вызывать функции-члены для несуществующего объекта или потому, что это обеспечивает большую оптимизацию нестатических функций-членов).

user17732522 21.07.2024 20:16

да, похоже, что на самом деле виноват nullptr, а не вызов через указатель. Исправление того, что каждый компилятор работает: godbolt.org/z/E74bYvonh

Gene 21.07.2024 20:22

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