Минимальный пример:
#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): прекрасно компилируется (любой уровень оптимизации и -Wall -Wextra -pedantic)
ICC / MSVC: прекрасно компилируется (пробовал с разными версиями на godbolt.org)
CLANG (v4 ... v6): ошибка: выражение static_assert не является целочисленным константным выражением
static_assert (v.MAX == 10, "");
РЕДАКТИРОВАТЬ (перефразируя вопрос):
По моему мнению, поведение clang наименее удивительно (или более интуитивно). Учитывая, что это единственный компилятор, который не может скомпилировать приведенный выше код, я хотел бы понять, какое из 2 поведений корректно и почему?
РЕДАКТИРОВАТЬ 2:
Судя по этому добавлению шаблонной функции, gcc ищет использование объявленного типа параметра и определяет, какой член constexpr использовать независимо от того, что было передано.
При передаче по значению clang также оценивает MAX как постоянное выражение. В этом случае очевидно, что v.MAX == 10 будет верным для всех компиляторов для не шаблонной функции.
РЕДАКТИРОВАТЬ 3 (еще более короткая версия):
Который до сих пор не компилируется на 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;
}