constexpr и ODR

Если у нас есть файл заголовка widget.hpp со следующим содержанием:

constexpr int foo = 10;

struct widget
{
    int bars[foo];
};

... и у нас есть две единицы перевода, созданные из двух исходных файлов, оба из которых включают только widget.hpp, нарушает ли это одно правило определения (точнее, нарушает ли использование foo одно правило определения)?

foo имеет внутреннюю связь, но это также постоянное выражение. Судя по тому, что я прочитал 3.2.6 в стандарте C ++ 11, который я процитирую ниже, это хорошо сформировано, если требование № 2 не относится исключительно к статическим элементам данных.


3.2.6 требование №2:

в каждом определении D соответствующие имена, найденные в соответствии с 3.4, должны относиться к объекту, определенному в определении D, или должны относиться к тому же объекту после разрешения перегрузки (13.3) и после сопоставления частичной специализации шаблона (14.8 .3), за исключением того, что имя может относиться к энергонезависимому константному объекту с внутренней связью или без нее, если объект имеет один и тот же буквальный тип во всех определениях D, и объект инициализируется константным выражением (5.19 ), и объект не используется odr, и объект имеет одно и то же значение во всех определениях D


person Simple    schedule 17.06.2013    source источник


Ответы (3)


Единственное место, где я могу найти место для вопросов по вашему делу, - это то, квалифицируется ли ваше использование foo как odr-used или нет. Возможно, самый простой способ прояснить хотя бы намерение - это процитировать соответствующий раздел n1337 (сразу после официального стандарта, в основном очищая некоторые фразы, как в этом случае):

[...] имя может относиться к константному объекту с внутренней связью или без нее, если объект имеет один и тот же буквальный тип во всех определениях D, и объект инициализируется константным выражением (5.19), а значение (но используется не адрес) объекта, и объект имеет одно и то же значение во всех определениях D;

Ваше использование явно соответствует всем этим требованиям.

  1. foo во всех случаях имеет тип int.
  2. foo инициализируется постоянным выражением.
  3. вы используете только значение, а не адрес foo.
  4. foo имеет одинаковое значение во всех определениях widget.

Тем не менее, вам, вероятно, лучше было бы вместо этого заменить foos на std::vector:

struct widget { 
    std::vector<int> bars;

    widget : bars(foo) {}
};
person Jerry Coffin    schedule 17.06.2013
comment
Джерри, вы проверяете, что он не использовал указатель foo; Что, если бы он раньше использовал указатель на foo где-то еще, тогда его исходное выражение все еще использовалось бы odr? - person Abdurrahim; 08.12.2018

Что касается множественных определений foo, я не думаю, что foo используется odr, потому что он удовлетворяет требованиям для появления в постоянном выражении согласно 3.2.3:

Переменная x, имя которой отображается как потенциально оцениваемое выражение ex, используется odr, если x не является объектом, который удовлетворяет требованиям для появления в постоянном выражении.

Поэтому, поскольку он не используется odr, правило odr к нему не применяется, 3.2.4:

Каждая программа должна содержать ровно одно определение каждой не встроенной функции или переменной, которая используется odr в этой программе.

Что касается двух разных определений widget и достаточно ли они похожи в соответствии с 3.2.6. Ответ положительный, потому что N3485 3.2.6:

имя может относиться к константному объекту [да, constexpr is const] с внутренним [constexpr is internal] или без связи, если объект имеет тот же буквальный тип [да, оба int] во всех определениях D, и объект инициализирован с постоянным выражением [да, 10], и используется значение (но не адрес) объекта, и объект имеет то же значение [да, 10] во всех определениях D

Таким образом, даже несмотря на то, что имя foo относится к двум различным объектам в двух разных TU, эти два объекта удовлетворяют заданным требованиям.

(На практике это работает, потому что компилятор идентично разметит два класса, поэтому сгенерированный код из двух TU будет совместим.)

person Andrew Tomazos    schedule 17.06.2013

Я не понимаю твоей интерпретации. Цитируемый текст правильно относится к процитированному примеру. Пока вы включаете это так во все ЕП. Чтобы нарушить ODR, вы должны что-то сломать в тексте:

constexpr double foo = 10; // type
constexpr int foo = ret_10(); // non-const init
constexpr int foo = 42; // value

И чтобы нарушить использование ODR, я думаю, вы должны взять его адрес.

person Balog Pal    schedule 17.06.2013