【问题标题】:Non-copyable elements in a std::vector of std::listsstd::lists 的 std::vector 中的不可复制元素
【发布时间】:2018-11-22 13:25:38
【问题描述】:

我有一个不可复制的类,简化如下:

struct NonCopyable
{
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator = (const NonCopyable&) = delete;
    NonCopyable(NonCopyable&&) = default;
    NonCopyable& operator = (NonCopyable&&) = default;
};

我将此类的对象存储在各种 std::lists 中。有一次,我有一个 empty std::vector 这些列表,我想调整它的大小以包含固定数量的 empty 列表。但是,这抱怨缺少 NonCopyable 复制构造函数,尽管(据我所知)它不应该尝试构造任何 NonCopyables!

std::vector<std::list<NonCopyable>> v; // OK
v.resize(4);                           // ERROR
v.emplace_back();                      // ERROR
std::list<NonCopyable> l;              // OK
v.push_back(l);                        // ERROR (unsurprisingly)
v.push_back(std::move(l));             // ERROR

这是为什么?有没有办法在不使类可复制的情况下做到这一点?

我正在使用带有 /std:c++17 的 VS2017,以防万一。


这是尝试调整大小的完整错误输出(上面的前两行)。

1>------ Build started: Project: testcore, Configuration: Debug x64 ------
1>testcard.cpp
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\xmemory0(881): error C2280: 'NonCopyable::NonCopyable(const NonCopyable &)': attempting to reference a deleted function
1>[...]\testcard.cpp(30): note: see declaration of 'NonCopyable::NonCopyable'
1>[...]\testcard.cpp(30): note: 'NonCopyable::NonCopyable(const NonCopyable &)': function was explicitly deleted
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(711): note: see reference to function template instantiation 'void std::_Default_allocator_traits<_Alloc>::construct<_Ty,const NonCopyable&>(_Alloc &,_Objty *const ,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Alloc=std::allocator<std::_List_node<NonCopyable,std::_Default_allocator_traits<std::allocator<NonCopyable>>::void_pointer>>,
1>            _Ty=NonCopyable,
1>            _Objty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(716): note: see reference to function template instantiation 'void std::_Default_allocator_traits<_Alloc>::construct<_Ty,const NonCopyable&>(_Alloc &,_Objty *const ,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Alloc=std::allocator<std::_List_node<NonCopyable,std::_Default_allocator_traits<std::allocator<NonCopyable>>::void_pointer>>,
1>            _Ty=NonCopyable,
1>            _Objty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(947): note: see reference to function template instantiation 'std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_List_buy<_Ty,_Alloc>::_Buynode<const NonCopyable&>(std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *,std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Alloc=std::allocator<NonCopyable>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(950): note: see reference to function template instantiation 'std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *std::_List_buy<_Ty,_Alloc>::_Buynode<const NonCopyable&>(std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *,std::_List_node<_Ty,std::_Default_allocator_traits<_Alloc>::void_pointer> *,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Alloc=std::allocator<NonCopyable>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(1308): note: see reference to function template instantiation 'void std::list<NonCopyable,std::allocator<_Ty>>::_Insert<const NonCopyable&>(std::_List_unchecked_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>,std::_Iterator_base0>,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(1308): note: see reference to function template instantiation 'void std::list<NonCopyable,std::allocator<_Ty>>::_Insert<const NonCopyable&>(std::_List_unchecked_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>,std::_Iterator_base0>,const NonCopyable &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(1265): note: see reference to function template instantiation 'void std::list<NonCopyable,std::allocator<_Ty>>::_Insert_range<_Iter>(std::_List_unchecked_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>,std::_Iterator_base0>,_Iter,_Iter,std::forward_iterator_tag)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Iter=std::_List_const_iterator<std::_List_val<std::_List_simple_types<NonCopyable>>>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(1264): note: see reference to function template instantiation 'void std::list<NonCopyable,std::allocator<_Ty>>::_Insert_range<_Iter>(std::_List_unchecked_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>,std::_Iterator_base0>,_Iter,_Iter,std::forward_iterator_tag)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Iter=std::_List_const_iterator<std::_List_val<std::_List_simple_types<NonCopyable>>>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(800): note: see reference to function template instantiation 'std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>> std::list<_Ty,std::allocator<_Ty>>::insert<std::_List_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,void>(std::_List_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,_Iter,_Iter)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Iter=std::_List_const_iterator<std::_List_val<std::_List_simple_types<NonCopyable>>>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(800): note: see reference to function template instantiation 'std::_List_iterator<std::_List_val<std::_List_simple_types<_Ty>>> std::list<_Ty,std::allocator<_Ty>>::insert<std::_List_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,void>(std::_List_const_iterator<std::_List_val<std::_List_simple_types<_Ty>>>,_Iter,_Iter)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable,
1>            _Iter=std::_List_const_iterator<std::_List_val<std::_List_simple_types<NonCopyable>>>
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\list(796): note: while compiling class template member function 'std::list<NonCopyable,std::allocator<_Ty>>::list(const std::list<_Ty,std::allocator<_Ty>> &)'
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\xmemory0(881): note: see reference to function template instantiation 'std::list<NonCopyable,std::allocator<_Ty>>::list(const std::list<_Ty,std::allocator<_Ty>> &)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\vector(1812): note: see reference to class template instantiation 'std::list<NonCopyable,std::allocator<_Ty>>' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\vector(1811): note: while compiling class template member function 'std::list<NonCopyable,std::allocator<_Ty>> *std::vector<std::list<_Ty,std::allocator<_Ty>>,std::allocator<std::list<_Ty,std::allocator<_Ty>>>>::_Udefault(std::list<_Ty,std::allocator<_Ty>> *,const unsigned __int64)'
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.15.26726\include\vector(1479): note: see reference to function template instantiation 'std::list<NonCopyable,std::allocator<_Ty>> *std::vector<std::list<_Ty,std::allocator<_Ty>>,std::allocator<std::list<_Ty,std::allocator<_Ty>>>>::_Udefault(std::list<_Ty,std::allocator<_Ty>> *,const unsigned __int64)' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>[...]\testcard.cpp(37): note: see reference to class template instantiation 'std::vector<std::list<NonCopyable,std::allocator<_Ty>>,std::allocator<std::list<_Ty,std::allocator<_Ty>>>>' being compiled
1>        with
1>        [
1>            _Ty=NonCopyable
1>        ]
1>Done building project "testcore.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 2 up-to-date, 0 skipped ==========

【问题讨论】:

    标签: c++ c++11 stl


    【解决方案1】:

    std::list 是 not noexcept-movable,但可以复制。因此,std::vector 更喜欢 list 的复制构造函数而不是其移动构造函数。

    要解决此问题,您必须将 std::list 包装在不可复制的类中。

    【讨论】:

    • 谢谢。如果 NonCopyable 类也不能移动怎么办? (我相信在列表之间移动对象仍然是可能的,因为它不会使引用无效,)
    • @UriZarfaty 可以使用splice 移动列表的节点,但std::vector 不知道。
    • @StoryTeller 这不完全是 QoI,而是从字面上遵循标准的问题。我在移动构造函数in the list synopsis 上没有看到noexcept 规范。也就是说,我同意 Clang 和 GCC 的行为更可取。
    • @j6t - 该标准也有值得商榷的 QoI。好吧,规范的质量会更准确,但这一点仍然成立。
    • 这些Requires 子句是针对程序员的指令,而不是针对编译器的指令。如果违反条件,你得到你应得的;充其量它不会编译,最坏的情况是它是未定义的行为。
    【解决方案2】:

    正如@j6t 指出的那样,似乎无法做到。带有不可复制元素的 std::list 的向量不能根据标准 (...) 展开。如果这是我的私人项目,我实际上会考虑这样的事情:

    #include <iostream>
    #include <vector>
    #include <list>
    
    namespace xxx {
        // <flame_bait>
        template<class T, class Enable = void>
        struct list : std::list<T> {
            list() : std::list<T>() {}
            list(list&&) = default;
            list& operator=(list&&) = default;
            list(const list&) = delete;
            list& operator=(const list&) = delete;
        };
    
        template<class T>
        struct list<T, typename std::enable_if<std::is_copy_constructible<T>::value>::type> : std::list<T> {};
        // </flame_bait>
    }
    
    struct NonCopyable
    {
        int m_id;
        NonCopyable(int id = 0) : m_id(id) {}
        NonCopyable(const NonCopyable&) = delete;
        NonCopyable& operator = (const NonCopyable&) = delete;
        NonCopyable(NonCopyable&&) noexcept = default;
        NonCopyable& operator = (NonCopyable&&) noexcept = default;
        ~NonCopyable() = default;
        friend std::ostream& operator<<(std::ostream&, const NonCopyable&);
    };
    
    std::ostream& operator<<(std::ostream& os, const NonCopyable& nc) {
        os << "I am not a free man, I am number " << nc.m_id;
        return os;
    }
    
    int main() {
        std::vector<xxx::list<NonCopyable>> v;
        v.resize(4);                    // now working 
    
        int id = 0;
        for (auto& l : v) {
            l.emplace_back(++id);       // emplace_back one NonCopyable per list  
        }
    
        xxx::list<NonCopyable> li;      // Create a separate list
        li.emplace_back(++id);          // create one...
        v.emplace_back(std::move(li));  // and move list to vector is now working
    
        for (auto& l : v) {
            for (auto& nc : l) {
                std::cout << nc << "\n";
            }
        }
    }
    

    预期输出:

    I am not a free man, I am number 1
    I am not a free man, I am number 2
    I am not a free man, I am number 3
    I am not a free man, I am number 4
    I am not a free man, I am number 5
    

    【讨论】:

    • 似乎我错过了v.resize(4); 失败的部分。该行的错误信息是什么?
    • error C2280: 'NonCopyable::NonCopyable(const NonCopyable &amp;)': attempting to reference a deleted function。我已将完整的错误输出添加到帖子中。
    • 太棒了!多么奇怪... :-) 回家后我会在 VS 中尝试一下。
    • 更糟的是:v.reserve(4); 给出了同样的错误。
    • 我添加了一个xxx::list 替代std::list,希望它可以作为可复制和不可复制对象的容器。我对模板很糟糕,所以请注意......
    猜你喜欢
    • 1970-01-01
    • 2016-03-30
    • 2022-10-24
    • 1970-01-01
    • 1970-01-01
    • 2011-12-27
    • 2018-01-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多