Невозможно использовать функцию `constexpr` члена как константу времени компиляции [duplicate]

«Одной из причин была лень».

Это звонит колокола. Единственная причина, по которой вы должны сделать что-то подобное, - это то, что вы знаете, как это сделать «правильно», но вы пришли к выводу, что есть осязаемая причина не делать этого таким образом.

сказал: если данные, которые вы предпочитаете хранить таким образом, - это данные, которые вам никогда не потребуется запрашивать, тогда может быть случай для его хранения в том виде, в котором вы выбрали.

(Некоторые пользователи будет оспаривать заявление в моем предыдущем абзаце, в котором говорится, что «вы никогда не сможете узнать, какие требования будут добавлены в будущем». Эти пользователи либо ошибочны, либо заявляют о религиозной убежденности. Иногда бывает полезно работать с требованиями, которые у вас есть перед вами .)

15
задан iammilind 11 May 2013 в 05:15
поделиться

3 ответа

Да, он плохо сформирован. Вот почему:

Функция constexpr должна быть определена (не просто объявлена) перед использованием в постоянном выражении.

Итак, например:

constexpr int f(); // declare f
constexpr int x = f(); // use f - ILLEGAL, f not defined
constexpr int f() { return 5; } // define f, too late
Определения функций

внутри спецификатора класса (а также инициализаторы и параметры по умолчанию) по существу разбираются в порядке, таком как они были определены вне класса.

Итак, это:

struct X {
  constexpr static int size() { return 5; }
  static const int array[size()]; 
};

Анализируется в следующем порядке:

struct X {
   constexpr inline static int size(); // function body defered
   static const int array[size()];  // <--- POINT A
};

constexpr inline int X::size() { return 5; }

То есть разбор тел функций откладывается до тех пор, пока не будет указан спецификатор класса.

Цель этой отсрочки разбора тела функции так что тела функций могут перенаправлять ссылочные классы, еще не объявленные в этой точке, а также поэтому они могут использовать свой собственный класс как полный тип:

struct X
{
    void f() { T t; /* OK */ }
    typedef int T;
};

По сравнению с областью пространства имен:

void f() { T t; /* error, T not declared */ }
typedef int T;

На POINT A компилятор еще не имеет определения size(), поэтому он не может его вызвать. Для производительности компиляции constexpr функции должны быть определены перед их использованием в блоке трансляции перед вызовом во время компиляции, иначе компилятор должен будет сделать несколько проходов только для «ссылки» постоянных выражений для оценки.

17
ответ дан Andrew Tomazos 21 August 2018 в 19:32
поделиться
  • 1
    Вы более или менее правильны. Формальный ответ заключается в том, что точка определения для функции-члена, написанная в строке, находится в конце определения класса. За исключением того, что я не думаю, что Стандарт когда-либо на самом деле выходит и говорит об этом. – Ben Voigt 11 May 2013 в 06:38
  • 2
    @BenVoigt: единственный способ реализовать это требование - отложить разбор тел функций до тех пор, пока спецификатор класса. Вам нужен поиск имени, чтобы разобрать C ++. Таким образом, в основном вы ограничиваете функции bodes, аргументы по умолчанию и инициализаторы - анализируете спецификатор класса - и затем анализируете ранее отложенные элементы с разделителями. – Andrew Tomazos 11 May 2013 в 06:48
  • 3
    Это все еще деталь реализации. Фактическое правило состоит в том, что класс является неполным в пределах собственного определения. Но должно существовать правило, запрещающее вызов статических функций-членов на неполном типе (при разрешении доступа к ранее объявленным статическим членам данных), и я не могу его найти. – Ben Voigt 11 May 2013 в 06:58
  • 4
    +1 для подробного объяснения, но я считаю, что это скорее ограничение в стандарте. Например, если глобальная функция объявлена ​​до ее использования и определена в конце файла, в таком случае компилятор дает такую ​​же ошибку. Почему компилятор не может применить ту же стратегию к члену static. Если он определен как упомянутый в моем коде, тогда разрешите его использовать, иначе укажите ошибку. – iammilind 11 May 2013 в 13:23
  • 5
    @iammilind: потому что функции, которые определены внутри внутри спецификатора класса, должны иметь возможность использовать вещи, которые еще не объявлены в этом спецификаторе класса. Это не то же самое в области пространства имен. В области пространства имен тело функции анализируется как часть определения функции, как и следовало ожидать. Это не является ограничением стандарта, это неизбежное свойство C ++ «single pass». сборник дизайн. – Andrew Tomazos 11 May 2013 в 13:45

По-видимому, это даже не ошибка , потому что его статус RESOLVED INVALID, что означает, что люди, стоящие за GCC, и эта bugzilla, после рассмотрения проблемы, не думают, что это GCC bug.

Я напоминаю вам эту страницу, потому что есть ответ на это поведение в одном из связанных сообщений.

6
ответ дан user2348816 21 August 2018 в 19:32
поделиться
  • 1
    @chris это не так, стандарт говорит что-то о том, как эта структура должна быть написана и почему это дает вам ошибку. – user2348816 11 May 2013 в 05:43
  • 2
    +1 для нахождения точной ссылки на пример. Однако то, что они обсуждали, не представляется оправданным. Если методы участника могут быть объявлены и определены снаружи. Глобальные функции также могут быть объявлены и определены позже. Если компилятор может отличить более поздний случай как «когда давать ошибку, а когда нет», то почему бы и нет для первого случая? – iammilind 11 May 2013 в 05:55
  • 3
    @iammilind: Чтобы использовать глобальную функцию constexpr в постоянном выражении, она должна быть определена (не просто объявлена) выше ее использования в блоке перевода. – Andrew Tomazos 11 May 2013 в 06:01
  • 4
    Я думаю, что это дефект в Стандарте. Он должен прийти прямо и сказать, что функции класса участников не определены до конца определения класса. Вместо этого у нас есть правило, в котором говорится, что класс является неполным внутри собственного определения, но это не объясняет, почему статическая функция constexpr может использоваться, когда статический член данных constexpr может. – Ben Voigt 11 May 2013 в 06:41
  • 5
    @BenVoigt: Конечно, это так. См. Раздел 3.3.7. – Andrew Tomazos 11 May 2013 в 06:50
1
ответ дан Karl 4 November 2018 в 16:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: