MSVC和constexpr的功能参数?

Ant*_*ier 9 c++ language-lawyer constexpr c++11

这段代码用clang和gcc编译得很好.

template<size_t n>
struct N {
    static constexpr size_t v = n;
};

template<size_t n>
constexpr bool operator<(N<n>, size_t n2) {
    return n < n2;
}

template<typename N>
constexpr void foo(N v) {
    static_assert(v < 5);
}

int main()
{
    foo(N<3>{});
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我使用MSVC,我得到的错误v < 5不是常量表达式.我能理解为什么MSVC会这么想,但我认为这是错误的,而clang/gcc是对的.这是来自MSVC的错误吗?

Rak*_*111 5

是的,MSVC在这里是错误的.

代码结构良好似乎是违反直觉的,因为如何(可能v不是常量表达式)可能在常量表达式中使用?

那么为什么允许呢?首先,非正式地考虑,如果表达式求值为glvalue本身不是常量表达式,或者是一个在封闭表达式之外开始生命的变量([expr.const] p2.7),则表达式不是常量表达式.

第二,operator<constexpr.

现在,会发生什么是v < 5有效的常量表达式.要理解这一点,我们来看看表达式的评估.

我们有:

  1. v < 5 打电话给你 constexpr operator<
  2. 复制这两个参数(两个参数都是文字,没有参数计算到非constexpr对象)
  3. n2在评估中开始了它的生命v < 5并且是文字的
  4. n 是一个非类型模板参数,因此可用于常量表达式
  5. 最后,n < n2调用内置运算符.

所有这些都没有违反[expr.const] p2中的任何一点,因此生成的表达式实际上是一个用作参数的常量表达式static_assert.

这些类型的表达式称为转换常量表达式.

这是一个简化的例子:

struct Foo {
  constexpr operator bool() { return true; }
};

int main() {
  Foo f;
  static_assert(f);
}
Run Code Online (Sandbox Code Playgroud)