Невозможно скомпилировать со статическим constexpr в режиме до C ++ 17 - PullRequest
4 голосов
/ 10 июня 2019

Почему следующий минимальный пример не компилируется с c++11 и c++14, но компилируется в c++17 и c++2a?

#include <iostream>
#include <limits>
#include <vector>

// works:
// static constexpr int VALUE_LIMIT_A = std::numeric_limits<int>::max();

class Classy {
    // does not work in c++11 (constexpr introduced) nor c++14:
    // works if c++17 or newer:
    static constexpr int VALUE_LIMIT_A = std::numeric_limits<int>::max();
    int VALUE_LIMIT_B = std::numeric_limits<int>::max();

    public:
        explicit Classy();
        std::vector<int> classy;
};

Classy::Classy() {
    // does not work:
    classy.resize(3, VALUE_LIMIT_A);

    // works:
    // classy.resize(3, std::numeric_limits<int>::max());

    // works:
    // std::cout << VALUE_LIMIT_A;

    // works:
    // classy.resize(3, VALUE_LIMIT_B);
}

// required in c++11 and c++14
// constexpr int Classy::VALUE_LIMIT_A;

int main() {
    Classy classy{};

    for (const auto& elem : classy.classy) {
        std::cout << elem << ",";
    }
    std::cout << "\n";
}

Вот вывод с c++11:

$ g++ -std=c++11 main.cpp && ./a.out
/tmp/ccon7pPo.o: In function `Classy::Classy()':
main.cpp:(.text+0x31): undefined reference to `Classy::VALUE_LIMIT_A'
collect2: error: ld returned 1 exit status

Вот вывод с c++17:

$ g++ -std=c++17 main.cpp && ./a.out
2147483647,2147483647,2147483647,

Ответы [ 2 ]

4 голосов
/ 10 июня 2019

Поскольку начиная с C ++ 17 определение constexpr статического члена данных в области имен снова не требуется.

Если const non-inline (since C++17) статический член данныхor a constexpr static data member (since C++11) is odr-used , определение в области именного пространства все еще требуется, но оно не может иметь инициализатор.This definition is deprecated for constexpr data members (since C++17).

struct X {
    static const int n = 1;
    static constexpr int m = 4;
};
const int *p = &X::n, *q = &X::m; // X::n and X::m are odr-used
const int X::n;             // … so a definition is necessary
constexpr int X::m;         // … (except for X::m in C++17)

Если статический элемент данных объявлен constexpr, он неявно inline и его не нужно повторно объявлять в области пространства имен.Это повторное объявление без инициализатора (ранее требовалось, как показано выше) все еще разрешено, но не рекомендуется.(начиная с C ++ 17)

И обратите внимание, что std::vector::resize принимает второй параметр по ссылке;что VALUE_LIMIT_A используется odr-используется для classy.resize(3, VALUE_LIMIT_A);.

3 голосов
/ 10 июня 2019

Начиная с C ++ 17, с введением встроенных переменных, static constexpr члены данных являются неявно встроенными переменными:

[dcl.constexpr]

1 ... Функция или член статических данных, объявленные с помощью спецификатора constexpr, неявно являются встроенной функцией или переменной ([dcl.inline]) ...

Inlineпеременные, как встроенные функции, определены в каждой единице перевода, в которой они используются. И компилятор преобразует несколько определений в одно.Это означает, что в отличие от C ++ 14, нет необходимости явно предоставлять определение вне класса для переменной static constexpr ради ODR, об этом позаботится компилятор.

Вы можетеи сойти с рук в C ++ 14 тоже.Как и в другом ответе, resize использует в ODR элемент статических данных.Вы можете обойти это, хотя:

classy.resize(3, int(VALUE_LIMIT_A));

Хотя это выглядит излишним, оно фактически отличается от использования константы напрямую.Это создает временное целое число со значением от константы.Но это не ODR-использовать константу.Вместо этого временная привязка связана со ссылкой, поэтому проблема устранена.Хотя константу лучше определять в коде, предшествующем C ++ 17, этот прием можно использовать для адаптации кода, который вы не можете контролировать.

...