Как компиляция и связывание влияют на объявления constexpr в нескольких файлах на C++?

Я пытаюсь понять взаимосвязь между компиляцией, связыванием и определением значений constexpr в C++. Я знаю, что значения constexpr должны быть полностью определены во время компиляции, но некоторые обсуждения предполагают, что они определяются в течение всего процесса трансляции, который включает как компиляцию, так и связывание. Например, рассмотрим следующий отрывок из книги «Эффективный современный C++», который меня смутил:

«Концептуально, constexpr указывает на значение, которое не только постоянное, но и но также известен во время компиляции. Технически их значения определяется в ходе перевода, а перевод состоит не только из компиляция, но и связывание. Если вы не пишете компиляторы или компоновщики однако для C++ это на вас не повлияет, так что вы можете беспечно программе, как если бы значения объектов constexpr были определены во время компиляция».

В моем случае я написал следующий код в source.cpp:

#include <string_view>
constexpr std::string_view c = "Hello world";

constexpr  std::string_view  func()
{
    return c;
}

В main.cpp я попытался использовать упреждающие объявления для этих значений constexpr, но столкнулся с проблемами, которые помешали успешной компиляции.

#include <array>
#include <iostream>
#include <string_view>

constexpr  std::string_view  func();

int main()
{
    constexpr  std::string_view s = func();
    constexpr size_t l = s.size();
    std::array<int, l> arr;
    std::cout << arr.size() << '\n';
}

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

main.cpp:10:22: Constexpr variable 'l' must be initialized by a
constant expression main.cpp:10:28: initializer of 's' is not a
constant expression main.cpp:9:33: declared here

но когда я помещаю весь код в main.cpp, он компилируется.

Константы Constexpr, которые «разделяются» между источниками, должны быть помещены в файл заголовка inline constexpr std::string_view c{"Hello world!"};, и этот файл заголовка должен быть включен в источники, которым нужна строка. И в основном вы должны использовать static constexpr std::string_view s{func()};

Pepijn Kramer 24.07.2024 05:12

Стандарт C++ не делает различия между «компиляцией» и «связыванием» (фактически слова «компилятор» и «компоновщик» вообще не используются в нормативном тексте — в лучшем случае они используются в примечаниях или сносках). Нормативный текст в стандарте просто относится к этапам перевода (некоторые из которых могут быть реализованы тем, что вы бы назвали «компилятором» или «компоновщиком») и ничего не говорит о том, какие этапы перевода участвуют в оценке constexpr ценности. Таким образом, любые ответы на ваш вопрос будут в контексте конкретной цепочки инструментов.

Peter 24.07.2024 07:16

Возможно, автор имеет в виду тот факт, что extern int x; constexpr int *p = &x; действителен, хотя (в типичной реализации) адрес x не определен до связывания.

Nate Eldredge 24.07.2024 08:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
3
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Способ определения константных выражений и constexpr в стандарте проработан тщательно, поэтому компилятор должен иметь возможность выполнять константную оценку только на этапе компиляции.

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

В частности, даже если функция объявлена ​​constexpr, ее все равно можно использовать как часть вычисления константы только в том случае, если она определена (в той же единице перевода) перед константным выражением, в котором она используется. Это объясняет, почему ваш пример пытается инициализировать s вызовом func с ошибкой в ​​единице перевода, в которой отсутствует определение func.

Кроме того, даже если вы не объявили s и другие переменные как constexpr (что приводит к тому, что инициализация должна быть постоянным выражением или в противном случае компиляция не удастся), программа все равно будет IFNDR (неверно сформированная, диагностика не требуется), потому что constexpr на функции подразумевает inline, а функция inline должна быть определена (где-то) в каждой единице перевода, в которой она (odr-) используется.

Более того, программа все равно будет IFNDR, даже если вы добавите определение func к единице перевода main, поскольку переменные const (подразумеваемые constexpr) по умолчанию в C++ имеют внутреннюю связь. Таким образом, два определения func в двух единицах перевода будут относиться к разным объектам c, что делает эти два определения несовместимыми для целей ODR.

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