Используется ли статическая переменная constexpr odr? - PullRequest
0 голосов
/ 10 сентября 2018

С кодом ниже, 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?

1 Ответ

0 голосов
/ 10 сентября 2018

Правило: [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?

Нет. Оптимизация это круто.

...