0

Using Clang++ (v3.8.0), the following code fails to link due to sSomeValue being an undefined reference.

#include <iostream>

struct MyClass
{
    static constexpr int sSomeSize = 3;
    static constexpr int sSomeValue = 10;
};

int foo()
{
    int someArray[MyClass::sSomeSize] = {};
    std::fill(std::begin(someArray), std::end(someArray), MyClass::sSomeValue);
    return someArray[0];
}

int main()
{
    std::cout << foo() << std::endl;
}

More precisely:

clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out

/tmp/main-c8de0c.o: In function `foo()':
main.cpp:(.text+0x2): undefined reference to `MyClass::sSomeValue'
/tmp/main-c8de0c.o: In function `main':
main.cpp:(.text+0x16): undefined reference to `MyClass::sSomeValue'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

However, changing the definition of foo to

int foo()
{
    int someArray[MyClass::sSomeSize] = {MyClass::sSomeValue, MyClass::sSomeValue, MyClass::sSomeValue};
    return someArray[0];
}

does not exhibit the same linker error, even though sSomeValue is still being used.

What is going on here? Is the compiler doing some optimization around the std::fill call that I may not be aware of? Note that g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out compiles, links, and outputs 10 as expected with v6.3.0.

drglove
  • 638
  • 1
  • 6
  • 21

1 Answers1

1

The error is nothing to do with std::fill.

If you refer below documentation, it says: Reference variables can be declared constexpr (their initializers have to be reference constant expressions):

http://en.cppreference.com/w/cpp/language/constexpr

Slight modification of your code works fine. Just make the struct variables "const &" as said above.

#include <iostream>

struct MyClass
{
    static constexpr int const& sSomeSize = 3;
    static constexpr int const& sSomeValue = 10;
};

int foo()
{
    int someArray[MyClass::sSomeSize] = {};
    std::fill(std::begin(someArray), std::end(someArray), MyClass::sSomeValue);
    return someArray[0];
}

int main()
{
    std::cout << foo() << std::endl;
}

Also refer here for well explained article about constexpr and static "Does static constexpr variable make sense?

Specially the last para in ticked answer.

Community
  • 1
  • 1
Pavan Chandaka
  • 11,671
  • 5
  • 26
  • 34
  • Thanks for the answer. `constexpr` is relatively new to me, so thank you for pointing out a solution! Still, I'm a little confused why only the `sSomeValue` member complains but `sSomeSize` seems valid to use as the array size parameter. Also, if the definition of my parameters was `static const int sSomeValue = 10;` I get the same linker error, even though I thought integral types were allowed to be given an initializer list. Can you shine any light on that? – drglove Apr 03 '17 at 23:50
  • Look at the definition of std::fill and the last parameter. it expects "const T&" http://en.cppreference.com/w/cpp/algorithm/fill – Pavan Chandaka Apr 03 '17 at 23:54
  • May be when you declare "static const int sSomeValue = 10;" .. you are still using structure to access it. It is working fine for me with a static declaration "static const int sSomeValue = 10;" – Pavan Chandaka Apr 03 '17 at 23:59
  • just call it with out accessing from structure. It should work when you declare "static const int sSomeValue = 10;" . std::fill(std::begin(someArray), std::end(someArray), sSomeValue); – Pavan Chandaka Apr 04 '17 at 00:01
  • A simple example. write a function void bar(int i) { } and.... call bar(MyClass::sSomeSize) it works... Now modify the function to void bar(int& i) {} and call bar(MyClass::sSomeSize). You will understand the difference. – Pavan Chandaka Apr 04 '17 at 00:11
  • C++17 fixes this problem. The problem here is that you have not declared any storage for an object, so when you bind to a reference, that object must "exist" somewhere. It is the classic "declare it outside of the class too" issue with C++ statics pre-17. – David Stone Jun 12 '17 at 01:54