简体   繁体   中英

Undefined reference for unoptimised constexpr used as default parameter

I do not understand why the below code compiles with GCC optimised, but fails to link with "undefined reference to `base::A_VAL'" when unoptimised. Am I doing something dodgy? Is this something that's a compiler bug (never is)? This is with g++ 5.4.0 on Ubuntu.

base.h:

class base {
public:
    static constexpr unsigned int A_VAL{0x69U};
};

derived.h:

#include "base.h"
#include <iostream>

using namespace std;

class derived : public base
{
public:
    int some_func(void) {
        cout << "Some func" << endl;
        return 0;
    }
};

concrete.h:

#include "derived.h"

#include <utility>

class concrete : public derived
{
public:
    concrete(int a, std::pair<unsigned int, unsigned int> data = {A_VAL, A_VAL}) {
        some_func();
        std::cout << "First: " << data.first << " Second: " << data.second << endl;
    }
};

test.cpp:

#include "concrete.h"

int main (int argc, char *argv[])
{
    concrete c{1};

    c.some_func();
}

g++ -O2 -std=c++14 -o test test.cpp

Fine.

g++ -O0 -std=c++14 -o test test.cpp

/tmp/ccm9NjMC.o: In function `main':
test.cpp:(.text+0x23): undefined reference to `base::A_VAL'
test.cpp:(.text+0x28): undefined reference to `base::A_VAL'
collect2: error: ld returned 1 exit status

When optimizing GCC is probably able to determine (after inlining an constant folding) that the body of concrete 's constructor can be replaced pretty much by

some_func();
std::cout << "First: " << A_VAL << " Second: " << A_VAL << endl;

Since operator<< for the standard stream class takes integers by value, and A_VAL is a constant expression, the call above doesn't require there to be any storage for A_VAL . Its value is just plugged in. As such, GCC doesn't need there to be an out of class definition for A_VAL , as is normally required for static class members.

When not optimizing, GCC quite likely initializes the pair object. std::pair 's constructor takes objects by reference, and a reference requires an object to bind to. So the the definition of A_VAL becomes required and so the linker complains.

You need to define the object somewhere (pre C++17)

// At namespace scope
constexpr unsigned base::A_VAL;

Or switch to compiling as C++17. Then A_VAL (like all constexpr static members data) will be implicitly an inline variable, and the compiler will resolve its definition by itself.

I'm not sure how constexpr affecting this, but you just declared static class variable, but not defined it. Ie usually you need to have constexpr unsigned int base::A_VAL{0x69U}; somewhere in .cpp file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM