在 constexpr 上下文中使用时默认移动构造函数失败,
Default move constructor failing when used in a constexpr context,
我在 constexpr 上下文中 Visual Studio 2022 (/std:c++latest) 中的默认移动构造函数有问题。我在 Visual Studio 2019 中没有看到这个问题。我有两个问题:
- 是我的代码还是 Visual Studio 2022 不正确?如果我的代码不正确,那是为什么?
- gcc 或 clang 中是否存在类似问题? (我目前确实可以访问这些编译器。)
问题陈述
在下面的代码中,我有两个 true/false 开关:
DEFAULT_MOVE
和 CONSTEXPR
,因此给了我四个可能的程序。
DEFAULT_MOVE
控制默认移动构造函数还是
使用自制移动构造函数。
CONSTEXPR
控制 Container
的实例是否创建于
编译时或 运行-time.
当 DEFAULT_MOVE
和 CONSTEXPR
都为真时,代码 不会 编译,产生消息:
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
并查看您使用的是哪个版本的编译器。
我在 constexpr 上下文中 Visual Studio 2022 (/std:c++latest) 中的默认移动构造函数有问题。我在 Visual Studio 2019 中没有看到这个问题。我有两个问题:
- 是我的代码还是 Visual Studio 2022 不正确?如果我的代码不正确,那是为什么?
- gcc 或 clang 中是否存在类似问题? (我目前确实可以访问这些编译器。)
问题陈述
在下面的代码中,我有两个 true/false 开关:
DEFAULT_MOVE
和 CONSTEXPR
,因此给了我四个可能的程序。
DEFAULT_MOVE
控制默认移动构造函数还是 使用自制移动构造函数。CONSTEXPR
控制Container
的实例是否创建于 编译时或 运行-time.
当 DEFAULT_MOVE
和 CONSTEXPR
都为真时,代码 不会 编译,产生消息:
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 makesize_t
work withoutstd::size
.
Clang/LLVM v13 可以使用 -std=c++20
构建它,Visual C++ 也可以使用 /std:c++20
(godbolt 上的 19.30 是 VS 2022 的编译器)。
我尝试了 /std:c++latest
,它也有效。
您使用的全套编译器开关是什么?也可以尝试 cl -Bv
并查看您使用的是哪个版本的编译器。