简体   繁体   English

为什么`std :: pair <int, movable> `需要一个[删除]`const&`复制构造函数?

[英]Why does `std::pair<int, movable>` require a [deleted] `const&` copy constructor?

I'm busy testing an implementation of various generic algorithms and I'm using types with minimal support of provided functions. 我正忙着测试各种通用算法的实现,而我正在使用对提供的函数支持最少的类型。 I came across this weird setup when using a std::pair<T, movable> with some type T (eg, int ) and a movable type defined like this: 当使用std::pair<T, movable>和某些类型T (例如, int )时,我遇到了这种奇怪的设置,并且使用了这样定义的movable类型:

struct movable
{
    movable() {}
    movable(movable&&) = default;
    // movable(movable const&) = delete;
    movable(movable&) = delete;
};

The idea is have a type which is movable but not copyable. 这个想法有一种可移动但不可复制的类型。 That works great, eg, with expressions like this: 这很好用,例如,使用这样的表达式:

movable m1 = movable();
movable m2 = std::move(m1);

However, when trying to use this type as a member of std::pair<...> it fails! 但是,当尝试将此类型用作std::pair<...>的成员时,它会失败! To make get the code to compile it is necessary to add the delete d(!) copy constructor taking a movable const& (or have only that version). 为了使代码得到编译,有必要添加delete d(!)复制构造函数来获取movable const& (或只有该版本)。 The copy constructor taking a non- const reference is insufficient: 采用非const引用的复制构造const不足:

#include <utility>
auto f() -> std::pair<int, movable> {
    return std::pair<int, movable>(int(), movable());
}

What is going on here? 这里发生了什么? Is std::pair<...> overspecified by mandating that std::pair(std::pair const&) is = default ed? std::pair<...>通过强制要求std::pair(std::pair const&) = default编辑来指定?

The problem seems to be down to the specification of std::pair 's copy constructor (in 20.3.2 [pairs.pair] synopsis): 问题似乎是std::pair的拷贝构造函数的规范(在20.3.2 [pairs.pair]概要中):

  namespace std { template <class T1, class T2> struct pair { ... pair(const pair&) = default; ... }; } 

A quick check with my implementation implies that the obvious implementation copying the two members does not require the const& version of the movable copy constructor. 快速检查我的执行意味着明显实现复制两个成员并不需要const&该版本movable的拷贝构造函数。 That is, the offensive part is the = default on pair 's copy constructor! 也就是说,攻击部分是pair的复制构造函数的= default

std::pair copy constructor is declared as follows: std::pair copy构造函数声明如下:

pair(const pair&) = default;

By declaring this copy constructor for movable : 通过声明此复制构造函数为movable

movable(movable&) = delete;

you inhibit implicit creation of movable(const movable&) (so it's not even deleted, there's just no such constructor), thus this is the only copy constructor you have. 你禁止隐式创建movable(const movable&) (所以它甚至没有被删除,也就是没有这样的构造函数),因此这是你唯一的复制构造函数。 But std::pair copy constructor requires a copy constructor of its members to take const reference, so you get compile error. 但是std::pair复制构造函数需要其成员的复制构造函数来获取const引用,因此会出现编译错误。

If you add this: 如果你添加这个:

movable(movable const&) = delete;

or (better) just remove movable(movable&) = delete; 或(更好)只是删除movable(movable&) = delete; declaration, you now have the movable(movable const&) constructor, and because it's deleted, the std::pair copy constructor also becomes deleted. 声明,你现在有了movable(movable const&)构造函数,并且因为它被删除了, std::pair复制构造函数也会被删除。

Update : Let's consider a simpler example demonstrating the same issue. 更新 :让我们考虑一个更简单的例子来说明同样的问题。 This doesn't compile: 这不编译:

template <typename T>
struct holder {
    T t;
    // will compile if you comment the next line
    holder(holder const&) = default;
    // adding or removing move constructor changes nothing WRT compile errors
    // holder(holder&&) = default;
};

struct movable {
    movable() {}
    movable(movable&&) = default;
    // will also compile if you uncomment the next line
    //movable(movable const&) = delete;
    movable(movable&) = delete;
};

holder<movable> h{movable()};

It will compile if you comment the copy constructor of holder , because this is how implicit copy constructor generation works ( [class.copy]/8 : 如果你注释了holder的拷贝构造函数,它会编译,因为这是隐式拷贝构造函数生成的工作方式( [class.copy]/8

The implicitly-declared copy constructor for a class X will have the form 类X的隐式声明的复制构造函数将具有该表单

X::X(const X&)

if each potentially constructed subobject of a class type M (or array thereof) has a copy constructor whose first parameter is of type const M& or const volatile M& . 如果每个可能构造的类类型M (或其数组)的子对象具有复制构造函数,其第一个参数的类型为const M&const volatile M& Otherwise, the implicitly-declared copy constructor will have the form 否则,隐式声明的复制构造函数将具有该表单

X::X(X&)

That is, when you comment out the declaration holder(holder const&) = default; 也就是说,当你注释掉声明holder(holder const&) = default; the implicitly declared copy constructor of holder will have the form holder(holder&) . holder的隐式声明的复制构造函数将具有表单holder(holder&) But if you don't, T 's copy constructor has take const T& (or const volatile T& ) because this is what will be called in memberwise copy procedure described in [class.copy]/15 . 但是,如果你不这样做, T的复制构造函数已经采用const T& (或const volatile T& ),因为这将在[class.copy]/15描述的成员复制过程中[class.copy]/15

And if holder has a move constructor, it's even easier - if you comment out holder(holder const&) = default; 如果holder有一个移动构造函数,那就更容易 - 如果你注释掉holder(holder const&) = default; , the implicitly declared copy constructor of holder will be just deleted. ,隐藏声明的holder复制构造函数将被删除。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么std :: max按const&返回? - Why does std::max return by const&? 为什么std :: runtime_error :: what()返回const char *而不是std :: string const& - Why does std::runtime_error::what() return const char* instead of std::string const& 为什么 boost::is_same <int const&, boost::add_const<int &> ::值等于假?</int> - Why does boost::is_same<int const&, boost::add_const<int &>::value equal false? 是强制转换std :: pair <T1, T2> const&to std :: pair <T1 const, T2> const&safe? - Is casting std::pair<T1, T2> const& to std::pair<T1 const, T2> const& safe? 为什么std :: pair <const int, int> 不适用于某些STL容器? - Why std::pair<const int, int> does not work with some STL containers? 未定义对&#39;std :: string Helper :: ToString的引用 <int> (int const&)&#39; - undefined reference to 'std::string Helper::ToString<int>(int const&)' 为什么std :: map :: const_iterator在std :: for_each期间调用std :: pair构造函数,但是一个简单的for循环却没有? - Why does std::map::const_iterator call the std::pair constructor during a std::for_each, but a simple for loop does not? 为什么是std :: pair <int, int> 可从const std :: pair构造 <int, float> &? - Why is std::pair<int, int> constructible from a const std::pair<int, float>&? 为什么`std :: pair`允许使用用户定义的删除move构造函数从类类型的右值进行初始化? - Why does `std::pair` allow to initialize from an rvalue of class type with a user-defined deleted move constructor? 错误:对`cv::imread(std::string const&amp;, int)&#39;的未定义引用 - error: undefined reference to `cv::imread(std::string const&, int)'
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM