Может ли встроенная функция в заголовочном файле использовать константу, имеющую внутреннюю связь?

Рассмотрим следующий код:

const int a = 0;
const std::string b = "hi";

inline void f_a1()
{
    std::cout << a;
}

inline void f_b1()
{
    std::cout << b;
}

inline void f_a2()
{
    std::cout << &a;
}

inline void f_b2()
{
    std::cout << &b;
}

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

Мое понимание встроенных функций заключается в том, что они должны быть одинаковыми в каждой единице перевода.

Мое понимание констант, использованных выше, заключается в том, что они неявно static т.е. внутренняя связь. Это означает, что каждая единица перевода получает свою собственную копию.

Поскольку приведенные выше встроенные функции полагаются на эти константы, какие из этих функций верны?


person Neil Kirk    schedule 27.02.2015    source источник
comment
f_a1 и f_b1 верны, потому что они относятся к значению константы. Даже если компилятор создаст более одной константы, все они будут иметь одинаковое значение. Таким образом, даже если разные экземпляры функций используют разные фактические константы, это не сломает ожиданий от кода (кстати, большинство компиляторов будут складывать константу int непосредственно в сгенерированном коде, поэтому в вашей программе не будет фактической константы a ). Я думаю о двух других.   -  person Giulio Franco    schedule 27.02.2015
comment
@GiulioFranco, почему f_a2 pr f_b2 может быть неверным?   -  person Luchian Grigore    schedule 27.02.2015
comment
@LuchianGrigore Я не говорил, что они есть. Я думаю об этом. Если константы действительно статически связаны (в чем я не уверен), то компилятор может в конечном итоге иметь разные их экземпляры, которые будут иметь разные адреса, если только компоновщик не сможет унифицировать разные определения (что должно быть в случае ).   -  person Giulio Franco    schedule 27.02.2015
comment
@GiulioFranco Я ожидаю, что адреса будут другими. Почему это может быть проблемой?   -  person Luchian Grigore    schedule 27.02.2015
comment
@LuchianGrigore смотрите ответ от ecatmur.   -  person Giulio Franco    schedule 27.02.2015
comment
@GiulioFranco: в C++03 все четыре были бы незаконными, поскольку текст требует того, что упоминает Нил: не только текст функций должен быть одинаковым во всех единицах перевода, но и интерпретация, включая то, к чему разрешается каждый из идентификаторов. При включении в две единицы перевода a преобразуется в два разных объекта, и даже если два определения имеют одинаковую семантику, они не являются одним. Правила для константных выражений были изменены в С++ 11, и я не уверен, каково текущее состояние... Я верю, не уверен, что в С++ 11 f_a1 может быть допустимым, я не думаю, что остальные   -  person David Rodríguez - dribeas    schedule 27.02.2015


Ответы (1)


Если они включены в несколько единиц перевода, единственной действительной функцией является f_a1.

Соответствующее предложение — [basic.def.odr]/6, в котором говорится, что функция inline может появляться в нескольких единицах перевода, но только при условии, что:

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

Поскольку объекты const, они имеют внутреннюю связь согласно [basic.link]/3:

Имя с областью действия пространства имен (3.3.6) имеет внутреннюю связь, если оно является именем [...]
— энергонезависимой переменной, которая явно объявлена ​​как const или constexpr и не объявлена ​​ни явно как extern, ни ранее объявлена ​​для иметь внешнюю связь [...]

Однако получение адреса или формирование ссылки на переменную (например, для передачи аргумента) является использованием odr, поэтому f_a2 и f_b2 недействительны. f_b1 также недействителен, так как оператор вывода ostream для std::string принимает свой аргумент по ссылке; и даже если он принимает свой аргумент по значению, неявно вызываемый конструктор копирования будет принимать его аргумент по ссылке. f_a1 подходит, потому что оператор потокового вывода int принимает свой аргумент по значению, а копирование значения int const не является использованием odr.

person ecatmur    schedule 27.02.2015
comment
Я не уверен, что это справедливо для int const. - person Giulio Franco; 27.02.2015
comment
f_a1 не odr-использует константу a, если вы замените константу на: struct T { const int a = 0; }; /* no definition */ код все равно должен компилироваться нормально, так как std::cout << T::a не odr-use T::a - person David Rodríguez - dribeas; 27.02.2015
comment
3.2/2 Переменная, имя которой появляется как потенциально вычисляемое выражение, используется odr, если только она не является объектом, который удовлетворяет требованиям для появления в постоянном выражении (5.19) и преобразования lvalue-to-rvalue (4.1) применяется немедленно, поскольку используется следующий оператор: basic_ostream<charT,traits>& operator<<(int n); преобразование lvalue-to-rvalue применяется немедленно, и это использование не odr-use - person David Rodríguez - dribeas; 27.02.2015
comment
@DavidRodríguez-dribeas ах, хорошая мысль. Итак, f_a1 в порядке, но остальные недействительны. - person ecatmur; 27.02.2015
comment
@DavidRodríguez-dribeas Вы имеете в виду статическую константу в своей структуре? - person Neil Kirk; 27.02.2015