2

Today I encountered a some compiler behaviour which seemed rather strange to me. Consider the following code:

#include <iostream>
#include <algorithm>

struct test {
    static const int x = 1;
    static const int y = 2;
};

int main() {
    std::cout << std::min(test::x, test::y) << std::endl;

    return 0;
}

When compiled (with either g++ 5.4, 6.3 or clang++ 4.0), the linker complains that it can not resolve the two symbols test::x and test::y.

$ clang++ test.cpp -o test  
/tmp/test-3cd074.o: In function `main':
test.cpp:(.text+0xa): undefined reference to `test::x'
test.cpp:(.text+0x14): undefined reference to `test::y'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

However, when explicitly (and redundantly) casting the static members to their original type, the compilation succeeds:

int main() {
    std::cout << std::min(static_cast<const int>(test::x), static_cast<const int>(test::y)) << std::endl;

    return 0;
}

This leads to the expected result:

$ clang++ test.cpp  -o test && ./test  
1

Just outputting the variables via std::cout works without a cast, so does initializing another variable with the values:

std::cout << test::x << std::endl; // Works!
int z = test::y; // Works as well!

Now my question is: Why can't the linker resolve the symbols when the static members are not being casted? Why is accessing the members not a problem when accessing them outside of a template?

andreas-hofmann
  • 2,378
  • 11
  • 15
  • 3
    May be a better duplicate: https://stackoverflow.com/q/3025997/1896169 – Justin Sep 18 '17 at 19:18
  • `test::x` etc. are not symbols. – too honest for this site Sep 18 '17 at 19:20
  • Thanks Justin, the linked question helps to clear things up. However, I'm still wondering why casting the variables solves the problem as well. Do you have any insights on this? – andreas-hofmann Sep 19 '17 at 07:54
  • 1
    @andreas-hofmann Remember to tag someone with @ if you want them to see it. From the accepted answer, the reason why it works is because `static const int x = 1;` isn't a definition of `x`, it's a "constant-initializer". Basically, you could write `int myInt = test::x` and it would work just fine, because the compiler says, "Oh, I can just replace `test::x` with `1`". However, you can't take a reference to it, which is why passing it to `std::min` fails. When you cast it, you never take a reference to `test::x`. It forces `std::min`'s argument to bind to the temporary rather than to `test::x`. – Justin Sep 20 '17 at 21:32
  • Thanks again @Justin. Temporary gave me the right idea. – andreas-hofmann Sep 21 '17 at 12:51

0 Answers0