c++constexprstd-bitset

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


Solution

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

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