std::is_copy_constructable 对于 std::vector
std::is_copy_constructable for std::vector
我最近读到 this blogpost 为什么矢量必须是无条件可复制的,这样它才能支持不完整的类型。我理解从逻辑的角度来看这也是必要的,因为以下内容对可复制性具有循环依赖性:
struct Test {
std::vector<Test> v;
};
现在我想,是否至少可以尝试提供最好的信息。换句话说,当且仅当 T
是可复制构造的或不完整的时,std::vector<T>
是可复制构造的。所以 std::vector<std::unique_ptr<T>>
永远不会是可复制构造的,因为 std::unique_vector
是移动的,独立于 T
.
我得出以下解决方案:
#include <type_traits>
#include <memory>
template<class T, class = decltype(sizeof(int))>
struct is_complete : std::false_type {};
template<class T>
struct is_complete<T, decltype(sizeof(T))> : std::true_type{};
template<class T>
constexpr bool is_complete_v = is_complete<T>::value;
// Indirection to avoid instantiation of is_copy_constructible with incomplete type
template<class T, class = std::enable_if_t<is_complete_v<T>>>
struct copyable {
static constexpr bool value = std::is_copy_constructible_v<T>;
};
template<class T>
struct copyable<T, void> : std::true_type {};
template<class T>
struct Container {
template<class T1 = T, class = std::enable_if_t<copyable<T1>::value>>
Container(const Container &) {}
};
struct A;
struct B{};
static_assert(!is_complete_v<A>);
static_assert(is_complete_v<B>);
static_assert(std::is_copy_constructible_v<Container<A>>);
static_assert(std::is_copy_constructible_v<Container<B>>);
static_assert(!std::is_copy_constructible_v<std::unique_ptr<A>>);
static_assert(!std::is_copy_constructible_v<std::unique_ptr<B>>);
struct A{};
static_assert(!is_complete_v<A>);
godbolt
(所有 static_assert
s 编译)
现在我有三个问题(抱歉,如果它们有点不相关):
- 此代码是有效的标准 C++ 还是依赖于任何地方的未定义行为?
- 你觉得这个想法怎么样?
- 我首先为复制构造函数设置了 SFINAE 条件
!is_complete_v<T1> || std::is_copy_constructible_v<T1>
,但我必须添加间接寻址,否则 clang(不是 gcc)将不会编译,因为 std::is_copy_constructible
是用不完整的类型实例化的。 ||
不也短路模板的实例化吗?
关于1.,我认为应该没有UB。可能发生的部分是 sizeof(T)
,因为不应该将它与不完整的类型一起使用。但是使用 sizeof
的 SFINAE-ing 有着悠久的传统,因为它是唯一未评估的上下文,所以我认为这没问题。
关于 2.,我知道这使得 vector<T>
是否可复制构造非常脆弱,因为如果在不相关部分的某处添加一个完整的 T
的前向声明的代码,然后也检查它的完整性,这将改变 T
整个项目的完整性。我不确定可用信息的少量增加是否值得。
necessary also from a logical point of view, since the following has a circular dependency on copyability:
struct Test {
std::vector<Test> v;
};
这在逻辑上没有必要。函数a
可以调用函数b
,函数b
调用函数a
。前提是在Test的声明中遇到v的声明必须回答这个问题。在我们所知道的当前 C++ 中,这是必要的,但这是根据我们自己强加的各种规则得出的。
Is this code valid standard C++ or does it rely on undefined behavior anywhere?
UB。模板特化在不同的实例化点不能有不同的含义。具体来说,“... class 模板的静态数据成员可能在翻译单元中具有多个实例化点”,始终包括结尾 temp.point/7。除了其他地方之外,编译器可以在翻译单元的末尾随意实例化 is_complete<T>::value
。如果在不同的实例化点给出不同的答案,则程序格式错误。
因此您不能使用不完整但稍后会完整的类型实例化 is_complete
,例如 Test
。
我最近读到 this blogpost 为什么矢量必须是无条件可复制的,这样它才能支持不完整的类型。我理解从逻辑的角度来看这也是必要的,因为以下内容对可复制性具有循环依赖性:
struct Test {
std::vector<Test> v;
};
现在我想,是否至少可以尝试提供最好的信息。换句话说,当且仅当 T
是可复制构造的或不完整的时,std::vector<T>
是可复制构造的。所以 std::vector<std::unique_ptr<T>>
永远不会是可复制构造的,因为 std::unique_vector
是移动的,独立于 T
.
我得出以下解决方案:
#include <type_traits>
#include <memory>
template<class T, class = decltype(sizeof(int))>
struct is_complete : std::false_type {};
template<class T>
struct is_complete<T, decltype(sizeof(T))> : std::true_type{};
template<class T>
constexpr bool is_complete_v = is_complete<T>::value;
// Indirection to avoid instantiation of is_copy_constructible with incomplete type
template<class T, class = std::enable_if_t<is_complete_v<T>>>
struct copyable {
static constexpr bool value = std::is_copy_constructible_v<T>;
};
template<class T>
struct copyable<T, void> : std::true_type {};
template<class T>
struct Container {
template<class T1 = T, class = std::enable_if_t<copyable<T1>::value>>
Container(const Container &) {}
};
struct A;
struct B{};
static_assert(!is_complete_v<A>);
static_assert(is_complete_v<B>);
static_assert(std::is_copy_constructible_v<Container<A>>);
static_assert(std::is_copy_constructible_v<Container<B>>);
static_assert(!std::is_copy_constructible_v<std::unique_ptr<A>>);
static_assert(!std::is_copy_constructible_v<std::unique_ptr<B>>);
struct A{};
static_assert(!is_complete_v<A>);
godbolt
(所有 static_assert
s 编译)
现在我有三个问题(抱歉,如果它们有点不相关):
- 此代码是有效的标准 C++ 还是依赖于任何地方的未定义行为?
- 你觉得这个想法怎么样?
- 我首先为复制构造函数设置了 SFINAE 条件
!is_complete_v<T1> || std::is_copy_constructible_v<T1>
,但我必须添加间接寻址,否则 clang(不是 gcc)将不会编译,因为std::is_copy_constructible
是用不完整的类型实例化的。||
不也短路模板的实例化吗?
关于1.,我认为应该没有UB。可能发生的部分是 sizeof(T)
,因为不应该将它与不完整的类型一起使用。但是使用 sizeof
的 SFINAE-ing 有着悠久的传统,因为它是唯一未评估的上下文,所以我认为这没问题。
关于 2.,我知道这使得 vector<T>
是否可复制构造非常脆弱,因为如果在不相关部分的某处添加一个完整的 T
的前向声明的代码,然后也检查它的完整性,这将改变 T
整个项目的完整性。我不确定可用信息的少量增加是否值得。
necessary also from a logical point of view, since the following has a circular dependency on copyability:
struct Test { std::vector<Test> v; };
这在逻辑上没有必要。函数a
可以调用函数b
,函数b
调用函数a
。前提是在Test的声明中遇到v的声明必须回答这个问题。在我们所知道的当前 C++ 中,这是必要的,但这是根据我们自己强加的各种规则得出的。
Is this code valid standard C++ or does it rely on undefined behavior anywhere?
UB。模板特化在不同的实例化点不能有不同的含义。具体来说,“... class 模板的静态数据成员可能在翻译单元中具有多个实例化点”,始终包括结尾 temp.point/7。除了其他地方之外,编译器可以在翻译单元的末尾随意实例化 is_complete<T>::value
。如果在不同的实例化点给出不同的答案,则程序格式错误。
因此您不能使用不完整但稍后会完整的类型实例化 is_complete
,例如 Test
。