Linker sometimes complains about undefined ref. to static constexpr member

Linker sometimes complains about undefined ref. to static constexpr member

I've just run into a linker error which can be tracked down to the following minimal code

#include <iostream>
#include <vector>

template <size_t A, size_t B>
class some_class
{

private:
  std::vector<size_t> vec;

public:
  static constexpr size_t product = A * B;
  some_class() :
    vec ()
    // vec (1, product) /* Undefined reference */
  {
    // vec.push_back(product); /* Undefined reference */
    vec[0] = product; /* But this is fine!*/
  };
  
  size_t get(size_t loc) { return vec[loc]; }

};

int main()
{
  some_class<2,3> some_object;
  std::cout << some_object.get(0) << std::endl;
  return 0;
}

The above code compiles and links correctly under the latest release of icpc with -std=c++11.

The two commented-out lines obviously represent two other ways of achieving the same result, however swapping out "vec[0] = product" with either of the alternatives causes the linker to complain about missing reference to some_class::product.

Strangely enough, if one were to include BOTH the initialisation list AND the vec[0] assignment then the linker stops complaining again.

Could I get some confirmation if this is a compiler bug or if I'm doing something which violates the C++11 standard?

20 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

I should quickly add also that g++-4.7.1 with -std=c++11 (which is the latest version I have handy) does not complain in any of the cases shown above.

Isn't your class declaration wrong? You have a static member variable, one instance per class (not instance of each instance of class), who's value may vary dependent on the values of A, B in the construction. You may need to remove "static" from product. Consider:

some_class<2,3> some_object;
some_class<4,5> other_object;

what do you expect for the one instance of some_class::product?
Jim Dempsey

www.quickthreadprogramming.com

Not really. Since A and B are template parameters, some_class::product isn't even defined. You would actually have to call some_class<A,B>::product where A and B are compile-time constants.

Indeed, in the example code above the linker complains

Undefined symbols for architecture x86_64:
"__ZN10some_classILm2ELm3EE7productE", referenced from:
_main in test.o

where __ZN10some_classILm2ELm3EE7productE demangles to some_class<2ul, 3ul>::product as expected.

>>...Since A and B are template parameters, some_class::product isn't even defined...

I think you're mixing declaration with instantiation of a template object of type some_class. So, as soon as you've done this:
...
some_class[ 2, 3 ] some_object;
...
some instance must be created and the product member should be equal to 2 x 3. Anyway, it makes sense to look at your test case.

Thanks.

Agreed, in this case an instance of some_class<2,3> has been created. However, as defined the product member should have been fixed to 2*3 at compile time when the compiler instantiates the template (that is the point of static constexpr after all -- I am actually using this as part of a larger template metaprogram), not when the object is created.

To make this clearer, I could've simplified the code as follows

#include <iostream>

template <size_t A, size_t B>

class some_class

{

 public:

  static constexpr size_t product = A * B;

};

int main()

{

  std::cout << some_class<2,3>::product << std::endl;

  return 0;

}

Indeed, the above code would compile, link, and run correctly. Likewise, if I now change main() to

int main()

{

  std::vector<size_t> vec(1, some_class<2,3>::product);

  std::cout << vec[0] << std::endl;

  return 0;

}

Then once again the icpc linker complains, but g++ does not. Note that this time round I have not instantiated any object of type some_class<A,B> at all.

>>...this time round I have not instantiated any object of type some_class...

It is done in a declaration of the vector:
...
std::vector vec( 1, some_class< 2, 3 >::product );
...

>>where __ZN10some_classILm2ELm3EE7productE demangles to some_class<2ul, 3ul>::product as expected

I was under the (false) impression that some_calss<size_t,size_t>::product would be generated.

Does the failure occur in Debug build? (iow, is optimization involved)

If not (optimization is involved), then try adding an unused member function that uses product.

Jim Dempsey

www.quickthreadprogramming.com

It is done in a declaration of the vector:
...
std::vector vec( 1, some_class< 2, 3 >::product );
...

Only the static value product is referred to here, no actual object is instantiate.

Does the failure occur in Debug build? (iow, is optimization involved)

Failure still occurs with -O0 -g. I've been searching on SO about this and there's some confusion about the declaration, definition and initialization of constexpr static members. I am under the impression that the initialization value needs to be specified at the point of declaration (which is what happens in the code above), however some people also says this does not constitute a definition of said member (which I find a bit puzzling).

Inspired by the above logic, I tried adding

template <size_t A, size_t B> constexpr size_t some_class<A,B>::product;

outside the class definition. This forces icc to generate the external symbol which can be linked against. Note that gcc actually just evaluates product = 6 uses this in the generated binary code without any reference to the ::product symbol, which is what the behaviour I expected.

I guess the real question is: is gcc or the icc behaviour the non-standard one here.

>>>>It is done in a declaration of the vector:
>>>>...
>>>>std::vector vec( 1, some_class< 2, 3 >::product );
>>>>...
>>
>>Only the static value product is referred to here, no actual object is instantiate.

When in Debugger what will you see for an element v[0]? The declaration should call the constructor of the some_class to create the object before it is added to the vector.

The object is not added to the vector, only the size_t value of 6. (i.e. in all the versions of the above code I should be getting vec[0] = 6.)

Note that vec is a std::vector<size_t>, not std::vector<some_class>. 

I couldn't reproduce the problem with 'Undefined reference'. Verified with Intel C++ Compiler XE 13.1.0.149 [ IA-32 & X64 ] ( Update 2 ) on a Windows 7 Professional.

[ Version 1 - No Any problems ( Compiles & Works ) ]
...
public:
static constexpr size_t product = A * B;
some_class() : vec()
{
vec[0] = product;
};
...

[ Version 2 - No Any problems ( Compiles & Works ) ]
...
public:
static constexpr size_t product = A * B;
some_class() : vec( 1, product )
{
vec.push_back(product);
};
...

My question is why do you need additional member product?

>>...Could I get some confirmation if this is a compiler bug or if I'm doing something which violates
>>the C++11 standard?...

I think that additional verification for Intel C++ compiler on Linux is needed.

Thanks for the verification. I'm getting the error on icpc 13.0.2 20130314 on Mac OS X 10.8. This is the latest version I have access to on Intel's download page.

My question is why do you need additional member product?

This is actually part of a template metaprogram involving the new C++11 variadic templates. The 'real' code defines something like

template <size_t N...>

class some_class;

along with an auxiliary class with compile-time logic to compute the product from a list of size_t constants. I'm afraid there really isn't an alternative here other than defining a static constexpr.

Latest updates of Intel C++ compiler for Windows provide two command line switches to enable C++11 language support: /Qstd = { c++0x | c++11 }. Could you try c++0x instead of c++11?

This is a short follow up on:

>>...The object is not added to the vector, only the size_t value of 6. (i.e. in all the versions of the above code
>>should be getting vec[0] = 6 )...

You're right and I missed it.

Compiling with -std=c++0x doesn't change anything.

There used to be an old compiler issue relating to private and public. Try placing:

public:
static constexpr size_t product = A * B;

At the front of the class declaration (iow, in front of the private:)

Jim Dempsey

www.quickthreadprogramming.com

I've tried this and it works with latest 13.1 icl (x64) on SUSE with gcc 4.5:

#include <iostream>

#include <vector>

template <size_t A, size_t B>  

class some_class   {    

public:    

static constexpr size_t product = A * B;  

 };

int main()   {    

std::vector<size_t> vec(1, some_class<2,3>::product);    

std::cout << vec[0] << std::endl;    

 return 0;  

}

 

$icpc -std=c++11 tt.cpp

 

 

Jennifer

Also tried the code on the original post, it can be built fine as well on the same Suse Linux with gcc 4.5.

Jennifer

Leave a Comment

Please sign in to add a comment. Not a member? Join today