【问题标题】:Calling std::remove_if on list whose value_type is moveable but not copyable在其 value_type 可移动但不可复制的列表上调用 std::remove_if
【发布时间】:2014-03-15 19:49:22
【问题描述】:

我正在尝试这样的事情:

class MyClass
{
public:
 explicit MyClass(int) {...};
 MyClass(MyClass&& that) { swap(that); }
private:
 MyClass(const MyClass&); // disabled, pre-C++11 syntax
 MyClass& operator=(const MyClass&); // disabled, pre-C++11 syntax
};

现在我有一个列表,我通过 emplace 将它们插入其中,我正在尝试做这样的事情。

std::list<MyClass> lst;
std::remove_if(lst.begin(), lst.end(), [&,this](MyClass& mcl) { return mcl.is_foo();});

在 gcc 4.6.x 上,我不断收到此错误:

In file included from /usr/include/c++/4.6/algorithm:63:0,
             from simple_file_cache.cpp:5:
file_cache_entry.h: In function ‘_FIter std::remove_if(_FIter, _FIter, _Predicate)   
[with_FIter = std::_List_iterator<MyClass>, _Predicate = 
AnotherClass::foo_bar(std::tuple<unsigned int, unsigned int>)::<lambda(MyClass&)>]’:
anotherclass.cpp:225:11:   instantiated from here
anotherclass.h:68:18: error: ‘MyClass& MyClass::operator=(const MyClass&)’ is private
/usr/include/c++/4.6/bits/stl_algo.h:1149:13: error: within this context
make: *** [simple_file_cache.o] Error 1

为什么要找拷贝构造函数?

【问题讨论】:

  • std::removestd::remove_if?您的标题和代码不匹配。
  • 另请注意,GCC 4.6 中的 C++11 支持受到严格限制。
  • 实际上,std::remove_if 上的 std::list 对我来说没有意义。我宁愿使用std::list::erasestd::list::remove_if
  • “它为什么要寻找拷贝构造函数?” 不是。它正在寻找一个赋值函数。
  • 来自 C++ 标准(草案),[alg.remove]/6 关于remove_if: "注意: 范围[ret,last) 中的每个元素,其中@987654332 @ 是返回值,具有有效但未指定的状态,因为算法可以通过从最初在该范围内的元素移动来消除元素。”

标签: c++ gcc c++11


【解决方案1】:

您需要为remove_if 定义一个移动assignment-operator。如果存在用户声明的复制赋值运算符(或用户声明的复制 ctor、dtor 或 ...),则不会隐式声明。

下面好像是在g++4.6下编译的:

#include <list>
#include <algorithm>

class MyClass
{
public:
 explicit MyClass(int) {};
 MyClass(MyClass&&) {}
 MyClass& operator=(MyClass&&) {return *this;}
private:
 MyClass(const MyClass&); // disabled, pre-C++11 syntax
 MyClass& operator=(const MyClass&); // disabled, pre-C++11 syntax
};

int main()
{
    std::list<MyClass> lst;

    // compiles, but why use that:
    //std::remove_if(lst.begin(), lst.end(), [](MyClass& mcl) { return true; });

    // also compiles, makes more sense to me (but that depends on the context):
    lst.remove_if([](MyClass& mcl) { return true; });
}

请注意,如果可以保证,您应该考虑同时使用两个移动功能 noexcept

Live example


如果你想将列表的某些元素移动到列表的末尾,我宁愿使用基于splice 的算法。例如,

template<class value_type, class allocator, class F>
//typename std::list<value_type, allocator>::iterator
void
move_to_end_if(std::list<value_type, allocator>& list, F condition)
{
    if(list.size() < 2) return; //list.end();

    auto const former_last = std::prev(list.end());

    for(auto i = list.begin(); true; ++i)
    {
        if(condition(*i))
        {
            list.splice(list.end(), list, i);
        }

        if(i == former_last) break;
    }

    // return ????;
}

这会将所有满足条件的元素移动到列表的实际(当前)末尾,并保留它们的相对顺序。

注意:算法应该返回一个迭代器到未移动序列的末尾,或者list::end()。还没有找到一种优雅的方式来做到这一点。

【讨论】:

【解决方案2】:

@dyp 的帖子已经解决了 OP 的情况。

但对于其他来这里试图确定remove_if 是否会复制或移动值的人:

删除是通过移位完成的(通过复制赋值(C++11 前)移动赋值(C++11 起))(source)

如果您使用的是 C++ 11 或更新版本,并且向量值仅定义了复制分配,remove_if 将求助于使用复制分配。我在本地验证了这一点,但没有在网上找到文档。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-06
    相关资源
    最近更新 更多