Zoso
Zoso

Reputation: 3465

constexpr for values passed in by reference

I had this piece of code which compiles

#include <bitset>

struct A{
    std::bitset<50> b; };

void test(A a){
    static_assert(sizeof(int)*8 < a.b.size(), "can't accomodate int in bitset");
    int x = 5;
    a.b = x; }

int main(){
    A a;
    test(a); }

But this doesn't

#include <bitset>

struct A{
    std::bitset<50> b;
};

void test(A& a){
    static_assert(sizeof(int)*8 < a.b.size(), "can't accomodate int in bitset");
    int x = 5;
    a.b = x;
}

int main(){
    A a;
    test(a);
}

Fails with this error

const.cpp: In function ‘void test(A&)’: const.cpp:8:5: error: non-constant condition for static assertion
     static_assert(sizeof(int)*8 < a.b.size(), "can't accomodate int in bitset");
const.cpp:8:5: error: ‘a’ is not a constant expression

Why is a.b.size() not treated as a constexpr in the second case? Isn't the constexpr for std::bitset::size() the one that should be treated as const as per the reference? Or does the non-const reference passed in the second case trigger the compiler to generate the error?

Compiler version: g++ 4.8.4 on Ubuntu 14.0.4, compiled with g++ const.cpp -std=c++1y

Upvotes: 4

Views: 410

Answers (2)

YSC
YSC

Reputation: 40100

Deprecation Notice
This answer is now obsolete due to the changes in P2280: Using unknown pointers and references in constant expressions (proposal accepted into C++23 and applied as a defect report to C++11).

GCC 14 and more recent compilers accept the original code as valid. See https://godbolt.org/z/KE5G969ca.

A& a is not usable in constant expressions, making your program ill-formed.

The rule forbidding a.b.size() to be a constant expression with a being a A& is the following:

[expr.const]/3

A variable is usable in constant expressions after its initializing declaration is encountered if it is a constexpr variable, or it is of reference type or of const-qualified integral or enumeration type, and its initializer is a constant initializer.

In your case, the variable a is:

  • not declared constexpr (as a function argument, it wouldn't make sense),
  • and is not an integral or enumeration type,
  • and is not a reference whose initialization is a constant initializer:

[expr.const]/2

A constant initializer for a variable or temporary object o is an initializer for which interpreting its full-expression as a constant-expression results in a constant expression, except that if o is an object, such an initializer may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.


Take the following reduced example:

struct s { constexpr static bool true_value() { return true; } };
void assert_on(s const& ref)
{
    static_assert(ref.true_value());
}

int main()
{
    assert_on(s{});
}

gcc-9 wrongly accepts it1, but clang-8 produce the right diagnostic:

error: static_assert expression is not an integral constant expression

Full demo: https://godbolt.org/z/t_-Ubj


1) This is GCC bug #66477, active from version 5.1 and yet to be resolved.

Upvotes: 4

YSC
YSC

Reputation: 40100

With pointers from others1, it appears to be a compiler bug in both gcc and clang. It's fixed in gcc 5.1 but is, as of 2019-07-25, still a bug in clang 8.0

This is a bug of gcc, introduced in version 5.1. See other answer.


1)

Seems to be a (now fixed) compiler bug as it complies with the latest gcc version: https://godbolt.org/z/ZaDXED

Upvotes: 2

Related Questions