我试图将类模板的静态constexpr成员作为常量表达式访问,其中成员与所定义的类的类型相同。 MSVC和clang似乎都扼杀了这个简单的例子:
#include <iostream>
struct A
{
static A const DEFAULT;
constexpr A(int value = {}) : _value{ value } {}
constexpr operator int() const { return _value; }
int const _value;
};
constexpr A const A::DEFAULT{};
template<typename = void>
struct B_impl
{
static B_impl const DEFAULT;
constexpr B_impl(int value = {}) : _value{ value } {}
constexpr operator int() const { return _value; }
int const _value;
};
template<typename T>
constexpr B_impl<T> const B_impl<T>::DEFAULT{};
using B = B_impl<>;
template<int N>
constexpr int foo() { return N; }
int main(int, char**) noexcept
{
std::cout << "A: " << foo<A::DEFAULT>() << "\n"; // IntelliSense hates this
std::cout << "B: " << foo<B::DEFAULT>() << "\n"; // VC++ and clang die here:
// MSVC error: C2975 'N': invalid template argument for 'foo', expected compile-time constant expression
// clang error: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'N'
return 0;
}
gcc accepts both A
和B
作为常量表达式。 MSVC和clang (demo)接受A
作为常量表达式,并且对B
没有问题,除非我尝试将其用作常量表达式,然后失败。
我试图在这里使用模板的全部原因是在不强制执行C ++ 17的情况下实现内联变量,因为这是在头文件中使用的。
在这个剥离的例子中,我可以使用静态constexpr函数来存储constexpr值,然后按值返回(因为constexpr函数中不允许使用静态constexpr变量,因此我无法通过引用返回) 。实际的代码有点复杂,我没有展示,因为它混淆了底层问题(gcc demo)。我实际上是在尝试存储一个值数组(正在定义的类型,因此内联constexpr定义是不可能的),对这些值的命名引用,所有这些都应该是静态的constexpr
同样,这可以在所有三个编译器上工作,如果我省略了模板,我不想这样做,因为这是在标题中我是尝试避免破坏ODR。
MSVC和clang是否因为拒绝这个模板化版本而受到抨击,或者gcc是否允许某些内容无法通过?