Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't compile with static constexpr under pre-C++17 mode

Why doesn't the following minimal example compile with c++11 nor c++14, but compiles in c++17 and 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";
}

Here is output with 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

Here is output with c++17:

$ g++ -std=c++17 main.cpp && ./a.out
2147483647,2147483647,2147483647,
like image 527
sviamwvnb Avatar asked Jan 27 '23 05:01

sviamwvnb


1 Answers

Because since C++17, the definition of the constexpr static data member at namespace scope is not required again.

If a const non-inline (since C++17) static data member or a constexpr static data member (since C++11) is odr-used, a definition at namespace scope is still required, but it cannot have an initializer. 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)

If a static data member is declared constexpr, it is implicitly inline and does not need to be redeclared at namespace scope. This redeclaration without an initializer (formerly required as shown above) is still permitted, but is deprecated. (since C++17)

And note that std::vector::resize takes the 2nd parameter by reference; which cause VALUE_LIMIT_A to be odr-used for classy.resize(3, VALUE_LIMIT_A);.

like image 171
songyuanyao Avatar answered Jan 28 '23 17:01

songyuanyao