MSVC和constexpr的功能参数?

问题描述 投票:9回答:3

这段代码用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;
}

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

c++ c++11 language-lawyer constexpr
3个回答
-1
投票

如果您已声明:

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

但是,是的zxsw poi。

MSVC,Clang和GCC都会拒绝您的代码。原因是here被复制到v的第一个参数。这样的副本是对operator<的评价,而v不是一个常数表达式。

在你的情况下,v是一个空类。我不认为标准指定这样的类的复制构造函数是否应该访问对象1(N)的内存。因此编译器显示的行为取决于是否访问了空类对象的内存。

Clang和GCC在传递参数时不访问空类对象的内存,但是MSVC会这样做:看这个core language issue 1701

所以我认为所有编译器都是正确的。


1访问对象的内存表示以复制填充位将涉及compiler explorer link(或等效的),其在常量表达式中也是禁止的。


5
投票

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

代码结构良好似乎是违反直觉的,因为reinterpret_cast(不是常量表达式)怎么可能用在常量表达式中呢?

那么为什么允许呢?首先,非正式地考虑,如果表达式计算为glvalue本身不是常量表达式,或者是一个在封闭表达式之外开始生命的变量(v),则表达式不是常量表达式。

第二,[expr.const]p2.7operator<

现在,会发生的是constexpr是一个有效的常量表达式。要理解这一点,我们来看看表达式的评估。

我们有:

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

所有这些都没有违反operator<中的任何一点,因此得到的表达式实际上是一个常量表达式,可用作n2的参数。

这些类型的表达式称为v < 5

这是一个简化的例子:

n

0
投票

这里MSVC不正确,让我们从代码的简化版本开始:

n < n2

我们先看一下[expr.const]p2,我们是否违反了常量表达式的规则?如果我们看看static_assert

为文字类或constexpr函数调用除constexpr构造函数之外的函数[注意:过载分辨率(13.3)按常规应用 - 结束注释];

我们很好,converted constant expressions是constexpr函数,struct Foo { constexpr operator bool() { return true; } }; int main() { Foo f; static_assert(f); } 的复制构造函数是文字类的constexpr构造函数。

移动到struct N { static constexpr size_t v = 0; }; constexpr bool operator<(N n1, size_t n2) { return n1.v < n2; } void foo(N v) { static_assert(v < 5, ""); // C++11 does not allow terse form } 并检查比较static_assert,如果我们看看[expr.const]p2.2

除非适用,否则左值到右值的转换(4.1) - 一个整数或枚举类型的glvalue,它引用具有前面初始化的非易失性const对象,用常量表达式初始化,或者 - 文字类型的glvalue,引用用constexpr定义的非易失性对象,或引用此类对象的子对象,或者 - 一个文字类型的glvalue,它引用一个生命周期尚未结束的非易失性临时对象,用一个常量表达式初始化

我们在这里也很好。在原始示例中,我们指的是一个模板非类型参数,它可以在常量表达式中使用,因此同样的推理也适用于该情况。 operator<的两个操作数都是可用的常量表达式。

我们也可以看到N即使clang和gcc接受它。

© www.soinside.com 2019 - 2024. All rights reserved.