Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpression in derived class, clang vs rest

Tags:

c++

gcc

clang

Minimal example:

#include <cstddef>
struct B
{
    constexpr static const size_t MAX = 10;
};

struct D : B 
{
    constexpr static const size_t MAX = 20;
};

void use(const B& v)
{
    static_assert(v.MAX == 10, "");
}

template<typename X>
void use2(X&& v)
{
    static_assert(v.template MAX == 20, "");
}

int main ()
{
    D d;
    static_assert(d.MAX == 20, "");
    use(d);
    use2(d);

    return 0;
}

GCC (v5.4 ... v7.3): compiles fine (any level of optimization and -Wall -Wextra -pedantic) ICC / MSVC : compiles fine (tried with various versions on godbolt.org)

CLANG (v4 ... v6) : error: static_assert expression is not an integral constant expression static_assert(v.MAX == 10, "");

EDIT (rephrasing the question) :

In my opinion clang's behaviour is the least surprising one (or the more intuitive). Given it's the only compiler failing to compile the above code, I'd like to understand which of the 2 behaviours is correct and why?

EDIT 2 :

Judging by this addition of a template function, gcc looks to use the declared type of the parameter and work out which constexpr member to use regardless of what's passed in.

If passing by value, clang will also assess MAX as a constant expression. In this case it is obvious why v.MAX == 10 would be true for all compilers for non-templated function.

EDIT 3 (even shorter version): Which still does not compile on clang

#include <cstddef>

struct B
{
     constexpr static const size_t MAX = 10;
};

void use(const B& v)
{
    static_assert(v.MAX == 10, "");
}

template<typename X>
void use2(X&& v)
{
    static_assert(v.template MAX == 10, "");
}

int main ()
{
    B v;
    static_assert(v.MAX == 10, "");
    use(v);
    use2(v);

    return 0;
}
like image 916
da_m_n Avatar asked Apr 27 '18 15:04

da_m_n


1 Answers

Clang is correct.

v.MAX == 10 evaluates v, which is a reference without a preceding initialization. This is not allowed in a constant expression.

Note even if MAX is a static member, v is still evaluated during the evaluation of v.MAX.

You can use the class type to access MAX to avoid the evaluation of v, for example

void use(const B& v)
{
    static_assert(B::MAX == 10, "");
               // ^^^
}

template<typename X>
void use2(X&& v)
{
    static_assert(std::remove_reference_t<X>::MAX == 10, "");
               // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
like image 158
xskxzr Avatar answered Oct 01 '22 17:10

xskxzr