2012-06-26 3 views
9

Минимальный код:Является ли неопределенное поведение разными определениями встроенной функции?

// --------inline.h-------- 
struct X { 
    static inline void foo(); 
}; 
#ifdef YES 
inline void X::foo() { cout << "YES\n"; } 
#else 
inline void X::foo() { cout << "NO\n"; } 
#endif 

// --------file1.cpp-------- 
#define YES // <---- 
#include"inline.h" 
void fun1() 
{ 
    X::foo(); 
} 

// --------file2.cpp-------- 
#include"inline.h" 
void fun2() 
{ 
    X::foo(); 
} 

Если мы называем fun1() и fun2(), то они будут печатать YES и NO соответственно, что означает, что они имеют в виду различные функции тела же X::foo().

Независимо от того, это должно быть закодировано или нет, мой вопрос:
Является ли это четко определенным или неопределенным поведением?

+0

Если вы хотите иметь две разные свободные функции (не члены класса) в двух разных файлах, но с тем же именем, вы можете выделить их с помощью ключевого слова 'static' (или анонимного пространства имен). Пометка функции в файле2.cpp как статичная означает, что ни один другой файл .cpp не видит ее во время связывания. Это полезно для очень больших программ, где вы не всегда можете быть уверены, какие общие имена уже заняты функциями. – Crashworks

+0

Вам действительно не нужно inline.h и #define hack в этом случае; просто определите «void X :: foo()» (с встроенным или без него) одним способом в файле file1.cpp, а другой в файле file2.cpp, и вы будете иметь такое же поведение. – abarnert

+0

@abarnert: отказ от 'inline' приводит к другому нарушению одного правила определения. Нелинейные функции охватываются п. 3.2 абзаца 3, встроенные функции п. 3.2 параграфа 5. Многие (большинство?) Линкеров улавливают не-встроенные нарушения. Мина, по крайней мере, не улавливает встроенные нарушения. –

ответ

13

Да, это Неопределенное Поведение.

Ссылка:

C++ 03 Стандарт:

7.1.2 Функция спецификаторы [dcl.fct.spec]
Para 4:

Встроенная функция должна быть определена в каждой единицы перевода, в которой она используется, и должна иметь точно то же определение в каждом случае (3.2). [Примечание: вызов встроенной функции может быть встречен до того, как ее определение появится в блоке перевода. ] Если функция с внешней связью объявлена ​​встроенной в одну единицу перевода, она должна быть объявлена ​​встроенной во все единицы перевода, в которых она отображается; диагностика не требуется. Встроенная функция с внешней связью должна иметь одинаковый адрес во всех единицах трансляции. Статическая локальная переменная во внешней встроенной функции всегда относится к одному и тому же объекту. Строковый литерал во внешней встроенной функции - это один и тот же объект в разных единицах перевода.

Примечание: 3,2 относится к одному из определений правил, который гласит:

3,2 Одно из правил определения [basic.def.odr]
Абзац 1:

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

+0

+1. Также обратите внимание, что inline - это всего лишь подсказка (хотя она может также обмануть компилятор и/или компоновщик в отсутствие хорошей возможности предупредить вас в этом случае). – abarnert

+3

@abarnert: 'inline' - это нечто большее, чем подсказка. Если вызов функции заменяется на тело функции, это зависит от усвоения компилятора, но после использования' inline' компилятор ** должен ** следовать определенным правилам по одному определению Правило. –

+0

Да, но то, что эффективно говорят правила, заключается в том, что использование inline не может помочь вам в ODR, за исключением тривиального смысла, что вы можете повторить то же самое определение. – abarnert

4

Если мы называем fun1() и fun2(), то они не будут печатать ДА и НЕТ соответственно, что означает, что они имеют в виду различные функции тела же X :: Foo().

Вы попробовали? С разными уровнями оптимизации?

Я получаю ДА и НЕТ, ДА и ДА, или НЕТ и НЕТ в зависимости от уровня оптимизации и порядка, в котором скомпилированные объекты представлены компоновщику.

Излишне говорить, что это неопределенное поведение.

+0

+1 для этой информации. Я этого не замечаю. Я скомпилирован с '-O4'. – iammilind

+1

@ Als: Наблюдение за дико меняющимся поведением является хорошим доказательством (если не вполне доказательством) неопределенного поведения. Напротив, это опасно (соблюдение последовательного поведения не является хорошим доказательством того, что это не неопределенное поведение). – abarnert

+3

@Als: Я понимаю, что наблюдаемое поведение не является доказательством. Я знал, что это был UB заранее. Я подумал, что было бы интересно посмотреть, что было произведено, и посмотреть, как с ним поиграть, чтобы добиться отличных результатов. –