【问题标题】:Storing noncopyable (but movable) object in std::pair在 std::pair 中存储不可复制(但可移动)的对象
【发布时间】:2011-07-25 00:11:18
【问题描述】:

我正在尝试将不可复制(但可移动)的对象存储在 std::pair 中,如下所示:

#include <utility>

struct S
{
    S();
private:
    S(const S&);
    S& operator=(const S&);
};

int main()
{
    std::pair<int, S> p{0, S()};
    return 0;
}

但是 gcc 4.6 出现以下编译器错误:

In file included from include/c++/4.6.0/bits/move.h:53:0,
                 from include/c++/4.6.0/bits/stl_pair.h:60,
                 include/c++/4.6.0/utility:71,
                 from src/test.cpp:1:
include/c++/4.6.0/type_traits: In instantiation of 'const bool std::__is_convertible_helper<S, S, false>::__value':
include/c++/4.6.0/type_traits:789:12:   instantiated from 'std::is_convertible<S, S>'
src/test.cpp:13:31:   instantiated from here
src/test.cpp:7:5: error: 'S::S(const S&)' is private
include/c++/4.6.0/type_traits:782:68: error: within this context
In file included from include/c++/4.6.0/utility:71:0,
                 from src/test.cpp:1:
src/test.cpp: In constructor 'std::pair<_T1, _T2>::pair(_U1&&, const _T2&) [with _U1 = int, <template-parameter-2-2> = void, _T1 = int, _T2 = S]':
src/test.cpp:13:31:   instantiated from here
src/test.cpp:7:5: error: 'S::S(const S&)' is private
include/c++/4.6.0/bits/stl_pair.h:121:45: error: within this context

编译器似乎正在尝试调用std::pair&lt;_T1, _T2&gt;::pair(_U1&amp;&amp;, const _T2&amp;) 构造函数,这当然是有问题的。编译器不应该调用std::pair&lt;_T1, _T2&gt;::pair(_U1&amp;&amp;, _U2&amp;&amp;) 构造函数吗?这是怎么回事?

编辑:好的,我知道提供显式移动构造函数可以解决问题,但我还是有点困惑。

假设我通过从boost::noncopyable 继承而不是声明我自己的私有复制构造函数来使类不可复制。

以下工作正常,表明移动构造函数隐式生成的:

#include <boost/noncopyable.hpp>

struct S : boost::noncopyable
{
};

void f(S&&)
{

}

int main()
{
    f(S());
    return 0;
}

但是,std::pair 仍然不起作用:

#include <utility>
#include <boost/noncopyable.hpp>

struct S : boost::noncopyable
{
};

int main()
{
    std::pair<int, S> p{0, S()};
    return 0;
}

错误:

In file included from include/c++/4.6.0/utility:71:0,
                 from src/test.cpp:1:
/include/c++/4.6.0/bits/stl_pair.h: In constructor 'std::pair<_T1, _T2>::pair(_U1&&, const _T2&) [with _U1 = int, <template-parameter-2-2> = void, _T1 = int, _T2 = S]':
src/test.cpp:16:31:   instantiated from here
/include/c++/4.6.0/bits/stl_pair.h:121:45: error: use of deleted function 'S::S(const S&)'
src/test.cpp:4:8: error: 'S::S(const S&)' is implicitly deleted because the default definition would be ill-formed:
boost/boost/noncopyable.hpp:27:7: error: 'boost::noncopyable_::noncopyable::noncopyable(const boost::noncopyable_::noncopyable&)' is private
src/test.cpp:4:8: error: within this context

而且,添加= default-ed 默认构造函数和移动构造函数也无济于事!

#include <utility>
#include <boost/noncopyable.hpp>

struct S : boost::noncopyable
{
    S() = default;
    S(S&&) = default;
};

int main()
{
    std::pair<int, S> p{0, S()};
    return 0;
}

我得到同样的错误!我必须自己明确给出移动构造函数的定义,如果类有很多成员,这很烦人:

#include <utility>
#include <boost/noncopyable.hpp>

struct S : boost::noncopyable
{
    S() = default;
    S(S&&) {}
};

int main()
{
    std::pair<int, S> p{0, S()};
    return 0;
}

【问题讨论】:

  • 您没有定义移动构造函数!此外,在 C++0x 中,您可以说 =delete,因此您不必使用 private: 构造函数来禁用复制。
  • @Kerrek SB:不应该自动生成吗?
  • 不,移动构造函数不会为您隐式定义。不过,如果您不想输入默认值,可以使用 =default 请求默认值。
  • @Kerrek SB:在其他情况下,它们确实得到了隐式定义。 (我已经通过在成员对象的移动构造函数中放置一个 cout 对其进行了测试)。
  • 我相信只有在你没有定义任何复制构造函数或赋值运算符的情况下。当您获得隐式定义的构造函数时,有一套精心设计的规则。当然,例如 POD 有它们。

标签: c++ c++11 move-semantics std-pair


【解决方案1】:

您需要提供一个移动构造函数。以下编译没有错误。

#include <utility>

struct S
{
    S() {}
    S(S&&) {}
    S& operator=(S&&) {}

    S(const S&) =delete;
    S& operator=(const S&) =delete;
};

int main()
{
    std::pair<int, S> p{0, S()};
    return 0;
}


编辑:

似乎如果你从另一个类(或结构)继承,那么基础需要声明一个移动构造函数。我认为这是因为如果您 default 派生类的移动构造函数,它会尝试移动基对象但没有这样做。

这是一个编辑过的boost::noncopyable,它定义了一个移动构造函数。

#include <utility>

namespace boost {

namespace noncopyable_  // protection from unintended ADL
{
  class noncopyable
  {
   protected:
      noncopyable() {}
      noncopyable(noncopyable&&) {};
      ~noncopyable() {}
   private:  // emphasize the following members are private
      noncopyable( const noncopyable& );
      const noncopyable& operator=( const noncopyable& );
  };
}

typedef noncopyable_::noncopyable noncopyable;

} // namespace boost

struct S : boost::noncopyable
{
    S() = default;
    S(S&&) = default;

    S& operator=(S&&) {}
};

int main()
{
    std::pair<int, S> p{0, S()};
    return 0;
}

【讨论】:

  • 好的......但我仍然对幕后发生的事情感到有些困惑(请参阅我的编辑)
  • @HighCommander4 我已经编辑了我的答案。看起来目前唯一的解决方案是定义一个移动构造函数,直到 boost 开始为其类定义移动构造函数。
  • 我明白了,这是有道理的。但它仍然无法解释为什么我编辑的第一个示例(使用void f(S&amp;&amp;))有效......
  • @HighCommander4 嗯,也许编译器摆脱了对f() 的调用,因为它什么也没做。您可以在关闭优化的情况下尝试一下。也许在函数中声明一个 volatile 变量并对其进行处理?
  • 我已经关闭了优化。添加 volatile 变量也不会改变任何东西。我什至可以将移动构造函数声明为已删除并且它可以工作!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-08
  • 2023-04-10
相关资源
最近更新 更多