Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constexpr member function on l-value ref object: Clang and gcc disagree

When a class has a constexpr member function and that member function is being evaluated on an l-value object in a constexpr context, clang and gcc disagree whether the result is a constexpr value. Why? Is there a workaround that needs neither default-constructability nor copy-constructability?

When the object is passed by value, both compilers succeed compiling.

Clang versions trunk, 8, 7: static_assert expression is not an integral constant expression

and

Gcc versions trunk, 8.1, 7.4: compiles with no error

#include <array>

using A = std::array<int, 10>;

void foo(const A& a){
    // clang: static_assert expression is not an integral constant expression
    static_assert(a.size() > 0, "");
}


void foo2(A a){
    // this compiles on both clang and gcc
    static_assert(a.size() > 0, "");
}

// Some custom code with the same symptom:
class B{
  public:
    constexpr int size()const{
        return 42;
    }
};

void foo3(const B& b){
    // clang: static_assert expression is not an integral constant expression
    static_assert(b.size() > 0, "");
}


void foo4(B b){
    // this compiles on both clang and gcc
    static_assert(b.size() > 0, "");
}

https://godbolt.org/z/9vmyli

Workarounds with caveats:

void foo5(const B& b){
    // This works in clang, if the default constructor is defined
    static_assert(B().size() > 0, "");
}

void foo6(const B& b){
    // This works in clang, if the copy constructor is defined
    [](B b){static_assert(b.size() > 0, "");}(b);
}
like image 574
Jan15 Avatar asked Sep 03 '19 23:09

Jan15


1 Answers

As specified by the definition of core constant exprssions:

A core constant expression is any expression whose evaluation would not evaluate any one of the following:

...

  1. an id-expression referring to a variable or a data member of reference type, unless it was initialized with a constant expression or its lifetime began within the evaluation of this expression

...

So you are not allowed to have reference const A& a here.

As an example, the following snippet compiles fine:

using A = std::array<int, 10>;

constexpr A arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
constexpr const A& arr_ref = arr;   // OK: arr_ref is initialized with a constant expr

static_assert(arr.size() > 0, "");
static_assert(arr_ref.size() > 0, "");
like image 86
ph3rin Avatar answered Oct 02 '22 11:10

ph3rin