Используется ли статическая переменная constexpr odr?

Приводя ниже код, используется Foo::FOO1 ODR или нет?

#include <iostream>
#include <map>
#include <string>

class Foo
{
public:
    static constexpr auto FOO1 = "foo1";
    void bar();
};

void Foo::bar()
{
    const std::map<std::string, int> m = {
        {FOO1, 1},
    };
    for (auto i : m)
    {
        std::cout << i.first << " " << i.second << std::endl;
    }
}

int main()
{
    Foo f;
    f.bar();
    return 0;
}

Компиляция кода с -O1 или выше, это нормально, но если компилировать с -O0, я получаю ошибку ниже (см. coliru пример:

undefined reference to `Foo::FOO1'

что указывает на то, что используется ODR. Что он?


Я знаю, что приведенный выше код отлично работает с -O, но в реальный (и более сложный) случай:

  • Код компилируется и прекрасно связывается с -O2
  • Код получает указанную выше ошибку undefined reference LinkTimeOptimization (-O2 -flto)

Значит, это означает, что и оптимизация (-O), и LinkTimeOptimization (-flto) повлияют на правило использования ODR? Изменится ли это между C++ 14 и C++ 17?

При компиляции с C++ 17 у меня работает. g ++ -std = C++ 17 -O0 -Wall -pedantic -pthread main.cpp && ./a.out

Mayur 10.09.2018 08:01

хм ... да, я должен был указать, что сейчас я использую C++ 14.

Mine 10.09.2018 08:03

Спецификатор constexpr, используемый в объявлении объекта или нестатической функции-члене (до C++ 14), подразумевает const. Спецификатор constexpr, используемый в объявлении функции или статической переменной-члена (начиная с C++ 17), подразумевает inline. en.cppreference.com/w/cpp/language/constexpr

Mayur 10.09.2018 08:06

До C++ 17 он не считался определенным, он только декларировался там. Выведенный тип усложняет эту задачу. Релевантно: stackoverflow.com/questions/38043442/…

Swift - Friday Pie 10.09.2018 10:08

Формулировка здесь меня сбивает с толку. "Каков правильный способ использования ODR [...]?" не имеет смысла для меня как вопрос (нет единственного правильного способа использовать что-то odr, и это странная цель) - похоже, что на самом деле вопрос в том, используется ли является odr или нет. Я отредактировал это - дайте мне знать, если вы не согласны.

Barry 10.09.2018 16:30
3
5
511
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Правило [basic.def.odr] / 4:

A variable x whose name appears as a potentially-evaluated expression ex is odr-used by exunless applying the lvalue-to-rvalue conversion to x yields a constant expression that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion ([conv.lval]) is applied to e, or e is a discarded-value expression ([expr.prop]).

Первая часть, очевидно, удовлетворена (FOO1 - это constexpr, поэтому преобразование lvalue-to-rvalue дает постоянное выражение без вызова нетривиальных функций), но является ли вторая?

Мы создаем 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-to-rvalue, и это не выражение отброшенного значения.

По сути, когда вы берете адрес чего-то, это может быть odr-use - у него должно быть определение. Итак, вам нужно добавить определение:

constexpr char const* Foo::FOO1;

Обратите внимание, что, с другой стороны, это:

std::string s = FOO1;

будет ли нет использоваться не по назначению. Здесь мы напрямую вызываем конструктор, принимающий параметр char const*, который будет преобразованием lvalue-to-rvalue.


В C++ 17 мы получили это новое предложение в [dcl.constexpr]:

A function or static data member declared with the constexpr specifier is implicitly an inline function or variable ([dcl.inline]).

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


Также обратите внимание, что тот факт, что программа компилируется и связывает, не означает, что переменная, не имеющая определения, не использовалась odr.

So it indicates that both optimizations (-O) and LinkTimeOptimization (-flto) would affect ODR-use rule?

Они не. Оптимизация такая крутая.

Итак, с -std=c++14 и -O код компилируется нормально, потому что он оптимизирует код, встроив переменную?

Mine 11.09.2018 03:59

Другие вопросы по теме