1

Here is some code:

#include<iostream>

using namespace std;

class ShowHello {
 public:
    string operator()(int x) {return "hello";}
};

template<typename F>
class A {
 private:
    static F f;
    static inline string foo(const int& x) {
        return f(x);
    }

 public:
    A() {
        cout << "foo: " << foo(10) << endl;
    }
};


int main(int argc, char *argv[])
{
    A<ShowHello> a;
    return 0;
}

And here is me compiling it with some optimizations:

$ g++ -std=c++11 -O1 -Wall test.cc -o test && ./test
foo: hello

And no optimizations

$ g++ -std=c++11 -O0 -Wall test.cc -o test && ./test
/tmp/ccXdtXVe.o: In function `A<ShowHello>::foo(int const&)':
test.cc:(.text._ZN1AI9ShowHelloE3fooERKi[_ZN1AI9ShowHelloE3fooERKi]+0x2a): undefined reference to `A<ShowHello>::f'
collect2: error: ld returned 1 exit status

What am I doing wrong?

Also

$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.5) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Barry
  • 286,269
  • 29
  • 621
  • 977
fakedrake
  • 6,528
  • 8
  • 41
  • 64
  • 5
    You *declare* the static member variable `f`, but you never *define* it. In the optimized build, it's probably optimized out since it's not used. – Some programmer dude Oct 25 '17 at 14:42
  • [`using namespace std;` is a bad practice](https://stackoverflow.com/q/1452721/2176813), never use it. – tambre Oct 25 '17 at 15:04

1 Answers1

5

You declare a static member variable f here:

static F f;

and then you odr-use it here:

static inline string foo(const int& x) {
    return f(x);
}

That requires a definition of f be available, but you haven't provided a definition - just the declaration. That makes the program ill-formed, no diagnostic required. However, in practice, a definition is only necessary if the linker actually needs it - and in this case, the optimized build simply inlined the call completely, without even bothering with either the static member or your class. Indeed, the instructions include fun things like:

    movl    $1819043176, 16(%rsp)  // this is "hell"
    movl    $5, %edx
    movl    std::cout, %edi
    movq    $5, 8(%rsp)
    movb    $111, 4(%rsi)          // this is 'o'

Cool stuff.

Anyway, the unoptimized build does actually rely upon having a definition of f because the call is actually made - and you don't have a definition, hence the linker error. The optimized build doesn't rely on f, but you should define it anyway, for correctness.

template <typename F>
struct A { ... };

template <typename F>
F A<F>::f;
Barry
  • 286,269
  • 29
  • 621
  • 977
  • In C++17, you’ll be able to skip the separate definition step, and instead both declare and define it inside the class with `inline static F f;`. If a static member variable is `constexpr` it is also automatically `inline`, which is nice. – Daniel H Oct 25 '17 at 14:52