最小的例子:
#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;
}
Run Code Online (Sandbox Code Playgroud)
GCC(v5.4 ... v7.3):编译好(任何级别的优化和-Wall -Wextra -pedantic)ICC/MSVC:编译好(在godbolt.org上尝试各种版本)
CLANG(v4 ... v6):错误:static_assert表达式不是整数常量表达式static_assert(v.MAX == 10,"");
编辑(改写问题):
在我看来,clang的行为是最不令人惊讶的(或更直观).鉴于它的失败编译上面的代码中仅有的编译器,我想了解哪两个行为是正确的,为什么呢?
编辑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;
}
Run Code Online (Sandbox Code Playgroud)
叮当是正确的。
v.MAX == 10评估v,这是一个没有预先初始化的引用。这在常量表达式中是不允许的。
请注意,即使MAX是静态成员,v在 的求值过程中仍会求值v.MAX。
您可以使用类类型来访问MAX以避免评估v,例如
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, "");
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
Run Code Online (Sandbox Code Playgroud)