n31*_*159 5 c++ templates copy-constructor sfinae incomplete-type
我最近阅读了这篇博文,了解为什么向量必须是无条件可复制的,以便它可以支持不完整的类型。我知道从逻辑的角度来看这也是必要的,因为以下内容对可复制性具有循环依赖性:
struct Test {
std::vector<Test> v;
};
Run Code Online (Sandbox Code Playgroud)
现在我想,是否至少可以尝试提供现有的最佳信息。换句话说,std::vector<T>当且仅当复制可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>);
Run Code Online (Sandbox Code Playgroud)
godbolt
(全部static_assert编译)
现在我有三个问题(抱歉,如果它们有点无关):
!is_complete_v<T1> || std::is_copy_constructible_v<T1>而无法编译。模板的实例化不也短路了吗std::is_copy_constructible?||关于1.,我认为不应该有UB。可能发生这种情况的部分是sizeof(T),因为不应该将其与不完整的类型一起使用。但 SFINAE-ing withsizeof有着悠久的传统,从那时起它是唯一未评估的上下文,所以我认为这是可以的。
关于2.,我知道这使得 a 是否可复制构造非常脆弱,因为如果在代码的不相关部分的某处vector<T>添加了一个原本完整的前向声明,然后还检查它的完整性,这将改变完整性T的T整个项目。我不确定可用信息的小幅增加是否值得。
从逻辑的角度来看也是必要的,因为以下内容对可复制性具有循环依赖性:
Run Code Online (Sandbox Code Playgroud)struct Test { std::vector<Test> v; };
这在逻辑上并没有必要。函数a可以调用函数b,函数又调用函数a。这是必要的,前提是你必须回答在 Test 声明中遇到 v 声明时的问题。在我们所知的当前 C++ 中,这是必要的,但这是我们自己强加的各种规则的结果。
该代码是有效的标准 C++ 还是它依赖于任何地方未定义的行为?
UB。模板特化在不同的实例化点不能有不同的含义。具体来说,“...类模板的静态数据成员可以在翻译单元内具有多个实例化点”,始终包括结束temp.point/7。is_complete<T>::value除了其他地方之外,编译器还可以在翻译单元的末尾自由实例化。如果在不同的实例化点给出不同的答案,则该程序是格式错误的。
因此,您不能使用is_complete不完整但稍后会完整的类型进行实例化,例如Test.