【问题标题】:Compiler error when enforcing c++11 move semantics强制执行 c++11 移动语义时出现编译器错误
【发布时间】:2014-02-14 07:07:16
【问题描述】:

我提炼了代码,最终得到了下面的代码。插入地图时出错...

感谢标记问题的人 - 希望他是第一个回答问题的人;)。

现在说真的,我的观点是:移动是在“尽力而为”的基础上完成的,并且无法确保使用它(就像这里的天真删除一样),这会造成很多混乱。对我来说,必须提供一个从未使用过的复制构造函数体来让编译器静音是很狡猾的。

当然,可能是编译器和我的stl之间的坏戏。让生活和呼吸 C++ 标准的人们来澄清。

#include <vector>
#include <map>
#include <iostream>
using namespace std;

struct FeedDataType
{
    FeedDataType() {};
    FeedDataType(FeedDataType&& ) = default;

#if 0
    // error: use of deleted function 'FeedDataType::FeedDataType(const FeedDataType&) - why?
    FeedDataType(const FeedDataType& ) = delete;
#else
    // Compiles ok but never called. But cannot mark it delete...
    FeedDataType(const FeedDataType& ) { cout << "Never called!" << endl; };
#endif
};

int main ()
{
    vector< FeedDataType > x;
    map<int, vector< FeedDataType > > mymap;
    mymap.insert( std::make_pair( 0, x ) );
}

错误:

In file included from /cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/vector:63:0,
             from play3.cpp:1:
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h: In instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 = FeedDataType; _Args = {const FeedDataType&}]':
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:77:3:   required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const FeedDataType*, std::vector<FeedDataType> >; _ForwardIterator = FeedDataType*; bool _TrivialValueTypes = false]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:119:41:   required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const FeedDataType*, std::vector<FeedDataType> >; _ForwardIterator = FeedDataType*]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:260:63:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const FeedDataType*, std::vector<FeedDataType> >; _ForwardIterator = FeedDataType*; _Tp = FeedDataType]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_vector.h:310:9:   required from 'std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = FeedDataType; _Alloc = std::allocator<FeedDataType>; std::vector<_Tp, _Alloc> = std::vector<FeedDataType>]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_pair.h:137:64:   required from 'constexpr std::pair<_T1, _T2>::pair(_U1&&, _U2&&) [with _U1 = int; _U2 = std::vector<FeedDataType>&; <template-parameter-2-3> = void; _T1 = int; _T2 = std::vector<FeedDataType>]'
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_pair.h:273:72:   required from 'constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&) [with _T1 = int; _T2 = std::vector<FeedDataType>&; typename std::__decay_and_strip<_T2>::__type = std::vector<FeedDataType>; typename std::__decay_and_strip<_T1>::__type = int]'
play3.cpp:24:44:   required from here
/cs/insight_san/tools/usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h:77:7: error: use of deleted function 'FeedDataType::FeedDataType(const FeedDataType&)'
play3.cpp:13:9: error: declared here

原帖:

我想澄清一下编译器错误(RedHat 上的 g++ 4.7.2)。 这是什么意思:只是想强制使用移动构造函数。

所以我写:

FeedDataType(FeedDataType&& ) = default;
FeedDataType(const FeedDataType& ) = delete;

唉,它不能编译(我会省去你完整的堆栈跟踪):

.../lib/gcc/x86_64-unknown-linux-gnu/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h:77:7: error: use of deleted function 'FeedDataType::FeedDataType(const FeedDataType&)'

在需要copy ctor的地方并不明显,我为它写了一个主体,以便在使用时让程序核心,然后查看堆栈跟踪:

FeedDataType(FeedDataType&& ) = default;
FeedDataType(const FeedDataType& ) { cerr << "Called" << endl; assert(false); }

好吧,程序编译并运行,无需输入复制 ctor 代码。那么,如果不使用,为什么不能删除复制ctor?如何确定正在使用什么?

其他我不明白的事情:

void FeedData::add( const FeedDataType & feedDataRow )
{
    // Is move used here? If not why not warn?
    some_vector.push_back( std::move(feedDataRow) );
}

这显然使用了复制构造函数。如果编译器发出警告/错误提示此处未使用移动构造,那会很好吗?在大型程序中,很容易错过 const 并相信 std::move 会完成这项工作。其实应该是编译器错误——feedDataRow 是 const,从它移动构造应该是不可能的吧?

使用非常量引用似乎更好,因为在这种情况下使用了移动构造函数。还是应该按价值通过这里?

我认为第一个问题是编译器错误,但希望得到知道发生了什么的人的启发。我在想移动语义很容易,但显然需要做我的功课。寻找 Meyers 先生关于 C++11 的书籍;)。

谢谢,

阿德里安

【问题讨论】:

  • std::vector 我相信按值存储元素,并在调用 push back 时在内部使用复制构造函数。现在我猜你想避免这样的副本,但我不确定这是否允许,老实说我对 c++11 的这个领域不是很熟悉。
  • 不是原来的编译时堆栈跟踪(你饶了我们)指向FeedData::add吗?
  • 您没有回答自己的问题吗?您正在尝试从 const 转移。这是不可能的,因此尝试复制。复制被禁用,您会收到编译错误。到目前为止,一切看起来完全合乎逻辑。问题出在哪里?
  • 仅仅因为一个特定的程序运行没有进入你的复制构造函数,并不意味着你的代码没有在某个地方调用它。
  • @n.m. - 我的帖子中有两个问题。第一个,为什么编译错误,仍然存在。对于第二个,我承认,我应该在 stackoverflow 中读过一点……其他人也问同样的问题,有些人提出了新的 std::force_move() 或类似的问题。但我认为这是一个有效的担忧:假设你习惯性地将其传递为 const& 然后使用 move()。如果没有真正好的眼睛或分析,某人不会知道正在发生昂贵的副本而不是移动。一些帖子讨论了这个......开始阅读;)......

标签: c++11 move-semantics


【解决方案1】:

好吧,程序编译并运行,无需输入复制ctor代码。
那么,如果不使用,为什么不能删除copy ctor呢?
如何确定正在使用什么?

它可能在可能使用的范围内

在以下代码中:

void foo() = delete;

void bar(bool b)
{
    if (b) { foo(); }
    // Other stuff
}

假设我们只调用了bar(false)foo没有被调用,但是bar还是使用了一个被删除的函数。

如果您点击错误链接,您将看到代码的哪一部分可能使用了复制构造函数。

【讨论】:

    【解决方案2】:

    std::move 大致意思是“尽可能移动,否则复制”(不完全是,但对于本次讨论而言足够接近)。这并不意味着“尽可能移动,否则恐慌”。前一种语义在任何地方都非常有用,包括几乎所有编写基于模板的通用组件的情况。后者仅在极少数情况下有用,即可能无法移动,并且可以复制,但您不想复制。

    该标准为您提供了前者的语义,但没有提供后者。如果您需要后者,您可以按照以下方式编写自己的:

    template< class T >
    typename std::remove_reference<T>::type&& my_move( T&& t )
    {
        return static_cast<typename std::remove_reference<T>::type&&>(t);
    }
    
    template< class T >
    typename std::remove_reference<T>::type&& my_move( const T& t )
    {
        static_assert(!std::is_lvalue_reference<T&>::value, "Move is requested, but only copying is possible");
    }
    

    【讨论】:

    • 我明白了。但为什么不将其命名为 try_move 或 Maybe_move?或者说是演员表?事实上,它不会自行移动……就像不移除的移除算法一样。为什么要选择新的 C++ 功能 + 标准 + Meyers 的书籍以便能够编码。不要误会我的意思,我是 C++ 粉丝,我承认人们应该阅读手册。让我们把它留在这里......另一方面,我已经编辑了我的帖子以显示问题所在。看起来必须添加一个虚拟体来使编译器静音。但是“你不为你不使用的东西付费”的原则呢?谢谢。
    【解决方案3】:

    复制构造函数被vector的复制构造函数使用,它被std::make_pair中调用的std::pair的构造函数使用。在您的小示例程序的给定运行中实际上没有调用它的原因是向量是空的。

    当您拨打make_pair 时,在x 上尝试std::move

    【讨论】:

    • 完美,这正是我们所需要的!随着 make_pair 中的移动,它可以干净地编译,并且没有不被调用但需要的狡猾的虚拟复制构造函数......现在很清楚,像往常一样睁开眼睛后。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 2022-07-15
    • 2010-09-18
    • 2012-07-03
    • 2019-05-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多