在 constexpr 上下文中使用时默认移动构造函数失败,

Default move constructor failing when used in a constexpr context,

我在 constexpr 上下文中 Visual Studio 2022 (/std:c++latest) 中的默认移动构造函数有问题。我在 Visual Studio 2019 中没有看到这个问题。我有两个问题:

问题陈述
在下面的代码中,我有两个 true/false 开关: DEFAULT_MOVECONSTEXPR,因此给了我四个可能的程序。

DEFAULT_MOVECONSTEXPR 都为真时,代码 不会 编译,产生消息:
error C3615: constexpr 函数 'Containerstd::u8string_view,3::Container' 不能产生常量表达式

但是,在三个开关组合中的任何其他组合下,代码 编译成功

我原以为代码在所有四种情况下都能编译。

#include <array>
#include <cassert>
#include <string_view>

//switches
#define DEFAULT_MOVE true
#define CONSTEXPR true

template<typename T, size_t N>
struct Container{
  std::array<T, N> x; 

  template<typename... Args>
  constexpr explicit Container(Args&&...args) : x{std::forward<Args>(args)...} {}

#if DEFAULT_MOVE
  //default move ctor
  constexpr Container(Container&&) noexcept = default;
#else
  //home-brew move ctor
  constexpr Container(Container&& other) noexcept : x{} {
    for (size_t n{0} ; T& e : other.x)
      x[n++] = std::move(e);
  }
#endif

  constexpr Container(const Container&) = delete;
  constexpr Container& operator=(Container&&) = delete;
  constexpr Container& operator=(const Container&) = delete;
  constexpr ~Container() = default;
};

using X = Container<std::u8string_view, 3>;
using Y = Container<X, 2>;

int main() {
#if CONSTEXPR
  // constexpr creation of a Y object
  constexpr Y a{X{u8"a",u8"b"}, X{u8"a"}};
  static_assert(a.x[0].x[1] == u8"b");

#else
  // runtime creation of a Y object
  const Y a{X{u8"a",u8"b"}, X{u8"a"}};
  assert(a.x[0].x[1] == u8"b");
#endif  
}

您应该使用:constexpr Container(Container&&) = default;。在大多数情况下,默认实现已经是 noexcept

来自cppreference

default constructors, copy constructors, move constructors that are implicitly-declared or defaulted on their first declaration unless:

  • a constructor for a base or member that the implicit definition of the constructor would call is potentially-throwing
  • a subexpression of such an initialization, such as a default argument expression, is potentially-throwing
  • a default member initializer (for default constructor only) is potentially-throwing

验证此类事情的最佳方法是使用 https://godbolt.org/。它让您可以快速尝试许多不同的编译器。

Add #include <cstddef> to make size_t work without std::size.

Clang/LLVM v13 可以使用 -std=c++20 构建它,Visual C++ 也可以使用 /std:c++20(godbolt 上的 19.30 是 VS 2022 的编译器)。

我尝试了 /std:c++latest,它也有效。

您使用的全套编译器开关是什么?也可以尝试 cl -Bv 并查看您使用的是哪个版本的编译器。