Optional-uninitialized class: std::is_trivially_constructible 对于非默认构造函数似乎不正确?
Optionally-uninitialized class: std::is_trivially_constructible seems incorrect for non-default constructor?
我觉得 std::is_trivially_constructible<T, Arg>
没有告诉我真相。一、上下文:
我有一个小向量class,VectorND<T, N>
。默认构造它默认构造它的成员,所以 VectorND<float, 2>{} == VectorND<float, 2>{float{}, float{}} == VectorND<float, 2>{0.f, 0.f}
。这是期望的行为。
然而,有时,对于性能关键代码,我想构造它们未初始化。我的想法是我可以使用这样的标签类型:
struct uninitalized_t {};
static constexpr uninitalized_t uninitalized;
...
VectorND(uninitalized_t) {}
...
VectorND<float, 2> x{uninitalized}; //< Tell x to be uninitialized.
我可以让它工作。首先,如果我这样做
template <typename T, std::size_t N>
class VectorND {
std::array<T, 2> x;
public:
VectorND() = default;
T& operator[](std::size_t i) { return x[i]; }
};
https://godbolt.org/z/oTvo33xEb
然后默认构造函数使数据未初始化。与 VectorND() {}
.
相同
如果我做到了 VectorND() : x{} {}
,那么数据将归零,使其不再像预期的那样容易构造。这是所需的默认行为。
但如果我添加 explicit VectorND(uninitalized_t) {}
,那么 VectorND<float, 2> x{uninitialized}
似乎未初始化:https://godbolt.org/z/j9KWhPT7n 这又是我想要的。但由于某种原因,std::is_trivially_constructible_v<VectorND<float, 2>, uninitalized_t>
是 false
。为什么?我试过了
VectorND() = default; // Or VectorND() {};
explicit VectorND(uninitalized_t) {}
和
VectorND() = default; // Or VectorND() {};
explicit VectorND(uninitalized_t) : VectorND() {}
我仍然得到 std::is_trivially_constructible_v<VectorND<float, 2>, uninitalized_t> == false
。 https://godbolt.org/z/3n1Mz9dWd
为什么?
简答
这是行不通的,因为这不是标准定义普通构造函数的方式。
琐碎与初始化(或缺少初始化)无关,它只是标准规定的一组要求,用于将构造函数定义为 琐碎——a编译器生成更好的代码并库作者进行优化的状态。
长答案
微不足道的状态,就C++标准而言,与是否成员初始化无关。琐碎的 副产品 是您可能会从进行琐碎值初始化的对象中获取未初始化的数据——但这并不意味着未初始化的数据就是琐碎的定义。
从形式上讲,该标准只是概述了非常具体的构造函数被认为是微不足道的标准:
默认构造函数由 class.default.ctor/3
定义
A default constructor is trivial if it is not user-provided and if:
- its class has no virtual functions ([class.virtual]) and no virtual base classes ([class.mi]), and
- no non-static data member of its class has a default member initializer ([class.mem]), and
- all the direct base classes of its class have trivial default constructors, and
- for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.
Otherwise, the default constructor is non-trivial.
Copy/Move 构造函数由 class.copy.ctor/11
定义
A copy/move constructor for class X is trivial if it is not user-provided and if:
- class X has no virtual functions ([class.virtual]) and no virtual base classes ([class.mi]), and
- the constructor selected to copy/move each direct base class subobject is trivial, and
- for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;
otherwise the copy/move constructor is non-trivial.
注:对operator=
和析构函数
也有类似的要求
但是,对于不是复制、移动或默认构造函数的自定义构造函数,没有任何实际定义是微不足道的。这意味着任何自定义构造函数不能被认为是微不足道的。
这也有效地意味着如果 std::is_trivially_constructible<T,Args...>::value
测试默认构造函数、复制构造函数或移动构造函数,则它只能计算为 true
。
为什么微不足道很重要?
存在“微不足道”的状态是因为它为编译器和库作者提供了更好的优化保证。 copy/moving 的简单构造函数相当于简单的 mov
指令来复制数据(而不是要求任何清理或重新布线
对象的构造函数通常可能需要)。
此外,如果一个类型满足足够多的简单要求,它可能是“可简单复制”的——这允许编译器和库作者简单地复制 [=14]周围的数据=] 而不是要求构造函数调用。这也可用于在不违反严格别名的情况下查看不同的对象表示。
我觉得 std::is_trivially_constructible<T, Arg>
没有告诉我真相。一、上下文:
我有一个小向量class,VectorND<T, N>
。默认构造它默认构造它的成员,所以 VectorND<float, 2>{} == VectorND<float, 2>{float{}, float{}} == VectorND<float, 2>{0.f, 0.f}
。这是期望的行为。
然而,有时,对于性能关键代码,我想构造它们未初始化。我的想法是我可以使用这样的标签类型:
struct uninitalized_t {};
static constexpr uninitalized_t uninitalized;
...
VectorND(uninitalized_t) {}
...
VectorND<float, 2> x{uninitalized}; //< Tell x to be uninitialized.
我可以让它工作。首先,如果我这样做
template <typename T, std::size_t N>
class VectorND {
std::array<T, 2> x;
public:
VectorND() = default;
T& operator[](std::size_t i) { return x[i]; }
};
https://godbolt.org/z/oTvo33xEb
然后默认构造函数使数据未初始化。与 VectorND() {}
.
如果我做到了 VectorND() : x{} {}
,那么数据将归零,使其不再像预期的那样容易构造。这是所需的默认行为。
但如果我添加 explicit VectorND(uninitalized_t) {}
,那么 VectorND<float, 2> x{uninitialized}
似乎未初始化:https://godbolt.org/z/j9KWhPT7n 这又是我想要的。但由于某种原因,std::is_trivially_constructible_v<VectorND<float, 2>, uninitalized_t>
是 false
。为什么?我试过了
VectorND() = default; // Or VectorND() {};
explicit VectorND(uninitalized_t) {}
和
VectorND() = default; // Or VectorND() {};
explicit VectorND(uninitalized_t) : VectorND() {}
我仍然得到 std::is_trivially_constructible_v<VectorND<float, 2>, uninitalized_t> == false
。 https://godbolt.org/z/3n1Mz9dWd
为什么?
简答
这是行不通的,因为这不是标准定义普通构造函数的方式。
琐碎与初始化(或缺少初始化)无关,它只是标准规定的一组要求,用于将构造函数定义为 琐碎——a编译器生成更好的代码并库作者进行优化的状态。
长答案
微不足道的状态,就C++标准而言,与是否成员初始化无关。琐碎的 副产品 是您可能会从进行琐碎值初始化的对象中获取未初始化的数据——但这并不意味着未初始化的数据就是琐碎的定义。
从形式上讲,该标准只是概述了非常具体的构造函数被认为是微不足道的标准:
默认构造函数由 class.default.ctor/3
定义A default constructor is trivial if it is not user-provided and if:
- its class has no virtual functions ([class.virtual]) and no virtual base classes ([class.mi]), and
- no non-static data member of its class has a default member initializer ([class.mem]), and
- all the direct base classes of its class have trivial default constructors, and
- for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.
Otherwise, the default constructor is non-trivial.
Copy/Move 构造函数由 class.copy.ctor/11
定义A copy/move constructor for class X is trivial if it is not user-provided and if:
- class X has no virtual functions ([class.virtual]) and no virtual base classes ([class.mi]), and
- the constructor selected to copy/move each direct base class subobject is trivial, and
- for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;
otherwise the copy/move constructor is non-trivial.
注:对operator=
和析构函数
但是,对于不是复制、移动或默认构造函数的自定义构造函数,没有任何实际定义是微不足道的。这意味着任何自定义构造函数不能被认为是微不足道的。
这也有效地意味着如果 std::is_trivially_constructible<T,Args...>::value
测试默认构造函数、复制构造函数或移动构造函数,则它只能计算为 true
。
为什么微不足道很重要?
存在“微不足道”的状态是因为它为编译器和库作者提供了更好的优化保证。 copy/moving 的简单构造函数相当于简单的 mov
指令来复制数据(而不是要求任何清理或重新布线
对象的构造函数通常可能需要)。
此外,如果一个类型满足足够多的简单要求,它可能是“可简单复制”的——这允许编译器和库作者简单地复制 [=14]周围的数据=] 而不是要求构造函数调用。这也可用于在不违反严格别名的情况下查看不同的对象表示。