Правило: [basic.def.odr] / 4 :
Переменная x
, имя которой появляется в качестве потенциально вычисляемого выражения ex
, используется odr от ex
, если не применяет преобразование lvalue-to-rvalue к x
дает константное выражение, которое не вызывает никаких нетривиальных функций, и, если x
является объектом, ex
является элементом набора потенциальных результатов выражения e
, где либо lvalue- Преобразование в значение ([conv.lval]) применяется к e
, или e
является выражением отброшенного значения ([expr.prop]).
Первая часть, очевидно, удовлетворена (FOO1
- это constexpr
, поэтому преобразование lvalue-в-значение дает константное выражение без вызова нетривиальных функций), но вторая ли?
Мы создаем map
. Соответствующий конструктор там принимает initializer_list<value_type>
, то есть initializer_list<pair<const string, int>>
. pair
имеет связку конструкторов , но здесь будет вызываться:
template <class U1, class U2>
constexpr pair(U1&& x, U2&& y); // with U1 = char const*&, U2 = int
Важной частью здесь является то, что мы не создаем напрямую string
, мы проходим через этот конструктор преобразования pair
, который включает в себя привязку ссылки к FOO1
. Это использование одр. Здесь нет преобразования lvalue в rvalue, и при этом это не выражение отброшенного значения.
По сути, когда вы берете адрес чего-либо, это использование odr - оно должно иметь определение. Таким образом, вы должны добавить определение:
constexpr char const* Foo::FOO1;
Обратите внимание, что, с другой стороны, это:
std::string s = FOO1;
не будет не использованием odr. Здесь мы непосредственно вызываем конструктор, принимающий параметр char const*
, который будет преобразованием lvalue в rvalue.
<ч />
В C ++ 17 мы получили это новое предложение в [dcl.constexpr] :
Функция или член статических данных, объявленные с помощью спецификатора constexpr, неявно являются встроенной функцией или переменной ([dcl.inline]).
Это ничего не меняет в использовании odr, FOO1
все еще используется в вашей программе. Но это делает FOO1
неявно встроенной переменной, поэтому вам не нужно явно добавлять для нее определение. Довольно круто.
<ч />
Обратите внимание, что только то, что программа компилируется и ссылается, не означает, что переменная, в которой отсутствует определение, не использовалась odr.
Значит, это означает, что обе оптимизации (-O) и LinkTimeOptimization (-flto) повлияют на правило использования ODR?
Нет. Оптимизация это круто.