简体   繁体   中英

constructor of derived class cannot be constexpr if base class contains array member

I want to define constexpr values of a derived type (SBar), using a constructor whose only argument is a variable of the base class (SFoo), which is simply used initialize the base.

This works fine when the base class has no array member. However, when I add an array the derived values can no longer be constexpr. A simple copy of the base class does yield constexpr results, though.

I have explicitly defaulted all copy and move constructors just to be safe.

test.cpp

#define USE_ARRAY


struct SFoo
    {
        constexpr SFoo() =default;
        constexpr SFoo(SFoo const&) =default;
        constexpr SFoo(SFoo      &) =default;
        constexpr SFoo(SFoo     &&) =default;
        constexpr SFoo& operator = (SFoo const&) =default;
        constexpr SFoo& operator = (SFoo      &) =default;
        constexpr SFoo& operator = (SFoo     &&) =default;

#       ifdef USE_ARRAY
            constexpr SFoo(int const (&array)[1]) :
                M_array{array[0]}
                {}

            int M_array[1] = {0};
#       else
            constexpr SFoo(int value) :
                M_value{value}
                {}

            int M_value = 0;
#       endif
    };


struct SBar : SFoo
    {
        constexpr SBar() =default;
        constexpr SBar(SBar const&) =default;
        constexpr SBar(SBar      &) =default;
        constexpr SBar(SBar     &&) =default;
        constexpr SBar& operator = (SBar const&) =default;
        constexpr SBar& operator = (SBar      &) =default;
        constexpr SBar& operator = (SBar     &&) =default;

        constexpr SBar(SFoo foo) : SFoo(foo) {}
    };


// Instances:

#       ifdef USE_ARRAY
    constexpr int arg[1] = {3};
#       else
    constexpr int arg = 3;
#       endif

constexpr SFoo foo(arg); // base "value" constructor is constexpr.
constexpr SFoo foo2(foo); // base copy constructor is constexpr.
constexpr SBar bar(foo); // (line 54): this line fails.

compiling with

clang++ -std=c++1z -c -o test.o test.cpp 

yields

test.cpp:54:16: error: constexpr variable 'bar' must be initialized by a constant expression
constexpr SBar bar(foo);
               ^~~~~~~~
1 error generated.

however, everything works if I don't define USE_ARRAY.

Does anyone know why this is happening?

(I know std::array can help, but I'd rather use native arrays and understand the underlying problem).

So for clang it looks like there are a couple of fixes for this. You can change:

constexpr SBar(SFoo foo) : SFoo(foo) {}

to take foo by const reference:

constexpr SBar(const SFoo &info) : SFoo(info) {}

The other fix that seems to work is comment out the following copy constructor in sFoo:

//constexpr SFoo(SFoo      &) =default;

I don't see immediately the language in the draft C++1z standard that make this change make sense.

On the other hand gcc complains about the copy constructor saying the implicit definitions would not be constexpr ( see it live ), for example:

error: explicitly defaulted function 'constexpr SFoo& SFoo::operator=(const SFoo&)' cannot be declared as constexpr because the implicit declaration is not constexpr
    constexpr SFoo& operator = (SFoo const&) =default;
                    ^

which is not obvious to me from my reading of 7.1.5 [dcl.constexpr] and 5.20 [expr.const] .

As far as I can tell from my reading of section 12.8p26 the implicitly defined copy/move assignment should be constexpr. So gcc seems incorrect here.

It's not due to the array member. It's due to the assignment operators. If you make the following changes the code will work for both CLANG and GCC:

struct SFoo {
  constexpr SFoo() = default;
  constexpr SFoo(SFoo const&) = default;
  constexpr SFoo(SFoo&&) = default;
  constexpr SFoo(int const (&array)[1]) : M_array{array[0]} {}

  int M_array[1] = {0};
};

struct SBar : SFoo {
  constexpr SBar() = default;
  constexpr SBar(SBar const&) = default;
  constexpr SBar(SBar&&) = default;
  constexpr SBar(SFoo info) : SFoo(info) {}
};

Live Demo

Your previous setting will work if instead of classical array you use a std::array :

struct SFoo {
  constexpr SFoo() = default;
  constexpr SFoo(SFoo const&) = default;
  constexpr SFoo(SFoo      &) = default;
  constexpr SFoo(SFoo     &&) = default;
  constexpr SFoo& operator = (SFoo const&) = default;
  constexpr SFoo& operator = (SFoo      &) = default;
  constexpr SFoo& operator = (SFoo     &&) = default;
  constexpr SFoo(std::array<int, 1> const &array) : M_array{array} {}

  std::array<int, 1> M_array = {};
};

Live Demo

No as for the reason why. I'm still searching...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM