【问题标题】:Default move constructor failing when used in a constexpr context,在 constexpr 上下文中使用时,默认移动构造函数失败,
【发布时间】:2022-01-22 00:55:44
【问题描述】:

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

  • 是我的代码或 Visual Studio 2022 不正确吗?如果我的代码不正确,那为什么?
  • 在 gcc 或 clang 中是否存在类似问题? (我目前确实可以使用这些编译器。)

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

  • DEFAULT_MOVE 控制默认移动构造函数还是 使用自制的移动构造函数。

  • CONSTEXPR 控制是否创建Container 的实例 编译时或运行时。

DEFAULT_MOVECONSTEXPR 都为真时,代码编译,产生消息:
error C3615: constexpr function '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  
}

【问题讨论】:

  • 此问题已在 Visual Studio 最新版本中得到解决

标签: c++ c++20 constexpr visual-studio-2022 move-constructor


【解决方案1】:

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

来自cppreference

默认构造函数、复制构造函数、移动构造函数在第一次声明时隐式声明或默认,除非:

  • 构造函数的隐式定义将调用的基类或成员的构造函数可能会抛出
  • 此类初始化的子表达式(例如默认参数表达式)可能会抛出
  • 默认成员初始化器(仅适用于默认构造函数)可能会抛出

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

添加#include &lt;cstddef&gt; 使size_t 在没有std::size 的情况下工作。

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

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

您使用的全套编译器开关是什么?也可以试试cl -Bv,看看你到底在用什么版本的编译器。

【讨论】:

  • 感谢您的提示。我应该想到神螺栓!我会尽快与您联系编译器设置。
  • Visual Studio v17.0.5 中的编译器版本为 19.30.30709 for x86)。
  • 开关是:/permissive- /ifcOutput "x64\Debug\" /GS /W4 /Gy /Zc:wchar_t /I"..\..\icu\icu4c\include" /Zi / Gm- /Od /sdl /Fd"\x64\Debug\util.pdb" /Zc:inline /fp:precise /U"NDEBUG" /D "_D​​EBUG" /D "_CRT_SECURE_NO_WARNINGS" /D "_LIB" / D "NOMINMAX" /errorReport:prompt /WX /Zc:forScope /Gd /Oi /MDd /std:c++latest /FC /Fa"x64\Debug\" /EHsc /nologo /Fo"x64\Debug\" / Fp"x64\Debug\util.pch" /diagnostics:column
猜你喜欢
  • 2016-07-23
  • 1970-01-01
  • 1970-01-01
  • 2016-12-16
  • 1970-01-01
  • 2020-11-22
  • 1970-01-01
  • 2018-02-02
  • 1970-01-01
相关资源
最近更新 更多