Что значит ODR-использование чего-либо?

Это только что возникло в контексте еще вопрос.

Очевидно, что функции-члены в шаблонах классов создаются только в том случае, если они используются ODR. Может кто-нибудь объяснить, что именно это означает. В статье в Википедии о правиле одного определения (ODR) не упоминается "использование ODR ".

Однако стандарт определяет это как

Переменная, имя которой отображается как потенциально оцениваемое выражение, odr-used, если только это не объект, который удовлетворяет требованиям для появления в постоянном выражении (5.19) и преобразовании lvalue-to-rvalue (4.1 ) применяется немедленно.

в [basic.def.odr].

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

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

Однако я не понимаю, как это правило работает с несколькими единицами компиляции? Все ли функции-члены создаются, если я явно создаю экземпляр шаблона класса?


person Sarien    schedule 28.10.2013    source источник
comment
Обратите внимание, что [basic.def.odr] / 6 применяется к функциям-членам шаблонов классов. Может быть более одного определения [...]   -  person dyp    schedule 28.10.2013
comment
Все ли функции-члены создаются, если я явно создаю экземпляр шаблона класса? Да, см. [temp.explicit] / 8 + 9   -  person dyp    schedule 28.10.2013


Ответы (2)


Это просто произвольное определение, используемое стандартом, чтобы указать, когда вы должны предоставить определение для объекта (в отличие от простого объявления). Стандарт не говорит просто «используется», потому что это может интерпретироваться по-разному в зависимости от контекста. И некоторое использование ODR на самом деле не соответствует тому, что обычно ассоциируется с «использованием»; например, виртуальная функция всегда используется ODR, если она не является чистой, даже если она фактически не вызывается нигде в программе.

Полное определение находится во втором абзаце §3.2, хотя он содержит ссылки на другие разделы для завершения определение.

Что касается шаблонов, использование ODR - это только часть вопроса; другая часть - создание экземпляра. В частности, §14.7 охватывает создание экземпляра шаблона. Но они связаны между собой: хотя текст в §14.7.1 (неявное создание экземпляра) довольно длинный, основной принцип заключается в том, что шаблон будет создан только в том случае, если он используется, и в этом контексте используется означает использование ODR. Таким образом, функция-член шаблона класса будет создана только в том случае, если она вызывается или если она виртуальная и создается экземпляр самого класса. Сам стандарт во многих случаях рассчитывает на это: std::list<>::sort использует < для отдельных элементов, но вы можете создать экземпляр списка над типом элемента, который не поддерживает <, если вы не вызываете для него sort.

person James Kanze    schedule 28.10.2013
comment
Может ли использование ODR совпадать с материализованными временными конструкциями? - person v.oddou; 24.05.2019

Проще говоря, odr-used означает, что что-то (переменная или функция) используется в контексте, где должно присутствовать определение этого.

e.g.,

struct F {
   static const int g_x = 2;
};

int g_x_plus_1 = F::g_x + 1; // in this context, only the value of g_x is needed.
                             // so it's OK without the definition of g_x

vector<int>  vi;
vi.push_back( F::g_x );      // Error, this is odr-used, push_back(const int & t) expect
                             // a const lvalue, so it's definition must be present

Обратите внимание, что приведенный выше push_back передан в MSVC 2013, это поведение не соответствует стандарту, оба gcc 4.8.2 и clang 3.8.0 завершились неудачно, сообщение об ошибке: undefined ссылка на F::g_x

person zhaorufei    schedule 08.08.2017
comment
Можно ли использовать odr-элемент статических данных, например vi.push_back( F::g_x ); в c ++? - person Rankaba; 22.11.2017
comment
Но rvalue также можно передать const int&? Может ли статический константный член быть ragarded как rvalue? - person scottxiao; 19.02.2018
comment
+1 для краткого вступительного предложения: простым словом odr-used означает, что что-то (переменная или функция) используется в контексте, в котором должно присутствовать определение этого. - person Paul Masri-Stone; 29.03.2018
comment
Я не понимаю, как вы компилируете этот код. Они в одном ТУ? Если да, то F :: g_x уже был определен до push_back, конечно, он пройдет. Не так ли? - person Lewis Chan; 24.08.2018
comment
@bigxiao rvalue также может быть передано Да, и временный объект создается компилятором и ссылкой, привязанной к этому объекту. OTOH при передаче lvalue, что означает передачу объекта, на который ссылается оценка lvalue: когда вы передаете lvalue, вы можете ожидать, что параметр в функции будет ссылаться на правильный объект. Компилятор не создает временный. Если вам нужен временный, сделайте его с operator+. - person curiousguy; 05.12.2018
comment
@LewisChan Нет, здесь static const int g_x = 2; - это декларация, а не определение. - person NotAProgrammer; 08.10.2020
comment
@NotAProgrammer Я так не думаю. Это сообщает о дублировании экземпляров. Я считаю, что вы имеете в виду этот случай вместо этого, где статический член не ' t определен, но только объявлен. Я думаю, что const неотъемлемые члены могут быть определены встроенными. - person Zoso; 13.10.2020
comment
@Zoso Ты смешиваешь две вещи. В обоих ваших примерах foo объявлен в классе, но не определен. Причина, по которой вы получаете ошибку в своем первом примере, указана в [ class.static.data] / 4 ... The member shall still be defined in a namespace scope if it is odr-used (6.3) in the program and the namespace scope definition shall not contain an initializer. Вы можете определять их встроенными, но ни один из ваших примеров не содержит спецификатора inline. - person NotAProgrammer; 13.10.2020
comment
@NotAProgrammer Хорошо, я немного запутался. Итак, объявление в this инициализируется? FWIK, инициализация подразумевает определение, не так ли? Потому что это означало бы, что для переменной было выделено какое-то хранилище? - person Zoso; 13.10.2020
comment
@Zoso Смотрите, я просто недавно ответил на аналогичный вопрос на самом деле - person NotAProgrammer; 13.10.2020
comment
@NotAProgrammer Это помогает! По сути, я сомневался в том, что вообще происходит со значением инициализатора, если это объявление, а не определение. Ваш ответ подтверждает, что он хранится в регистре, тогда как на самом деле это выделенная память здесь по определению. - person Zoso; 14.10.2020
comment
Поскольку соответствующее правило не требует диагностики, MSVC соответствует требованиям, даже если принимает этот код. - person Davis Herring; 28.01.2021