23
votes

I am trying to implement in a C++ class a constexpr member function which returns a template parameter. The code is supposed to be c++11 compatible. However I encounter compilation issues when the templated class also contains STL containers as data members such as std::vector (which are untouched by the constexpr member function).

A minimal example is given by the following code:


#include <vector>
#include <iostream>
#include <array>


template<size_t n>
struct A 
{

  constexpr size_t dimensions() const
  {
    return n;
  }
private:
  std::vector<double> a;
};


int main(int argc,char ** argv)
{
  auto a=A<3>();
  std::array<double,a.dimensions()> arr;

}

The code compiles correctly with the commands

g++ -std=c++14 -O3 quickTest.cpp -o test -Wall

clang++ -std=c++11 -O3 quickTest.cpp -o test -Wall

but fails when I use

g++ -std=c++11 -O3 quickTest.cpp -o test -Wall

with the error

quickTest.cpp:22:33: error: call to non-‘constexpr’ function ‘size_t A<n>::dimensions() const [with long unsigned int n = 3; size_t = long unsigned int]’
   std::array<double,a.dimensions()> arr;
                     ~~~~~~~~~~~~^~
quickTest.cpp:10:20: note: ‘size_t A<n>::dimensions() const [with long unsigned int n = 3; size_t = long unsigned int]’ is not usable as a ‘constexpr’ function because:
   constexpr size_t dimensions() const
                    ^~~~~~~~~~
quickTest.cpp:22:33: error: call to non-‘constexpr’ function ‘size_t A<n>::dimensions() const [with long unsigned int n = 3; size_t = long unsigned int]’
   std::array<double,a.dimensions()> arr;
                     ~~~~~~~~~~~~^~
quickTest.cpp:22:33: note: in template argument for type ‘long unsigned int’

Why does the code not compile with gcc -std=c++11 but does compile with clang++ -std=c++11 ? How could one make this code snippet work with older versions of gcc which many not support c++14/17, but only c++11 ?

I am using gcc 8.1.1 and clang 6.0.1

1
C++14 Relaxed constexpr restrictions so this is fine. Question is who is right gcc or clang? IMO gcc. - Marek R
If you enable more warnings, the compiler tells you the issue: godbolt.org/z/hRMsN0 warning: enclosing class of 'constexpr' non-static member function 'size_t A<n>::dimensions() const [with long unsigned int n = 3; size_t = long unsigned int]' is not a literal type [-Wpedantic], note: 'A<3>' is not literal because: 'A<3>' has a non-trivial destructor -> It's not about std::vector but just about non-trivial destructors. - Max Langhof
Solution is simple: Make dimensions a static function (and remove the const). godbolt.org/z/y_YuMU - Max Langhof

1 Answers

20
votes

C++11 had a rule [dcl.constexpr]/8:

... The class of which that function is a member shall be a literal type ([basic.types]).

struct A is not a literal type because of the vector, hence its non-static member functions cannot be constexpr.

So GCC is right to reject the code in C++11 mode.

C++14 removed that restriction.

The solution for C++11 is to declare dimensions() static:

  static constexpr size_t dimensions()
  {
    return n;
  }

Live demo