Если у нас есть заголовочный файл 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), Кроме
что имя может ссылаться на энергонезависимый объект const с внутренним или
нет связи, если объект имеет одинаковый литеральный тип во всех определениях
из D, и объект инициализируется с помощью постоянного выражения (5.19),
и объект не используется odr, и объект имеет то же значение в
все определения D
Единственное место, где я могу увидеть любую комнату для вопросов по вашему делу, — это использование вами или нет foo
квалифицируется как odr-used
или нет. Возможно, самый простой способ прояснить хотя бы намерение — это процитировать соответствующий раздел n1337 (немедленно следуя официальному стандарту, в основном убирая некоторые фразы, например, в этом случае):
[…] имя может ссылаться на константный объект с внутренней связью или без нее, если объект имеет одинаковый литеральный тип во всех определениях D, а объект инициализируется с помощью константного выражения (5.19) и значения (но не адрес) объекта используется, и объект имеет одинаковое значение во всех определениях D;
Ваше использование четко соответствует всем этим требованиям.
foo
имеет тип int
в каждом случае.foo
инициализируется с постоянным выражением.foo
,foo
имеет одинаковое значение во всех определениях widget
,Тем не менее, вы, вероятно, лучше изменить foos
к std::vector
вместо:
struct widget {
std::vector<int> bars;
widget : bars(foo) {}
};
Что касается нескольких определений 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 является константным] с внутренним [constexpr является внутренним] или без связи, если объект имеет одинаковый литеральный тип [да, оба int] во всех определениях D, и объект инициализирован с константным выражением [да, 10] и значением (но не адресом) объекта, и объект имеет одинаковое значение [да, 10] во всех определениях D
Так что, хотя название foo
ссылается на два разных объекта в двух разных TU, эти два объекта удовлетворяют заданным требованиям.
(На практике это работает, потому что компилятор разметит два класса одинаково, поэтому сгенерированный код из двух TU будет совместим.)
Я не понимаю вашу интерпретацию. Цитируемый текст относится к цитируемому примеру должным образом. До тех пор, пока вы включаете это так во всех TU. Чтобы нарушить ODR, вы должны разбить что-то из текста:
constexpr double foo = 10; // type
constexpr int foo = ret_10(); // non-const init
constexpr int foo = 42; // value
И чтобы прервать использование ODR, я думаю, вы должны взять его адрес.