Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
137 Views

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?

0 Kudos
19 Replies
Highlighted
Beginner
137 Views

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.

0 Kudos
Highlighted
137 Views

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

0 Kudos
Highlighted
Beginner
137 Views

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.

0 Kudos
Highlighted
Valued Contributor II
137 Views

>>...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.
0 Kudos
Highlighted
Beginner
137 Views

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.

0 Kudos
Highlighted
Valued Contributor II
137 Views

>>...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 ...
0 Kudos
Highlighted
137 Views

>>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

0 Kudos
Highlighted
Beginner
137 Views

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.

0 Kudos
Highlighted
Valued Contributor II
137 Views

>>>>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.
0 Kudos
Highlighted
Beginner
137 Views

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>. 

0 Kudos
Highlighted
Valued Contributor II
137 Views

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?
0 Kudos
Highlighted
Valued Contributor II
137 Views

>>...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.
0 Kudos
Highlighted
Beginner
137 Views

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.

0 Kudos
Highlighted
Valued Contributor II
137 Views

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?
0 Kudos
Highlighted
Valued Contributor II
137 Views

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.
0 Kudos
Highlighted
Beginner
137 Views

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

0 Kudos
Highlighted
137 Views

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

0 Kudos
Highlighted
Moderator
137 Views

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

0 Kudos
Highlighted
Moderator
137 Views

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

0 Kudos