简体   繁体   English

使用移动赋值调整向量大小:结果类型必须可从输入类型构造

[英]vector resize with move assignment: result type must be constructible from input type

I wanted to try to add a move assignment operator to my Mesh class below, knowing that a vector of Meshes appear as a field member in my Model class:我想尝试向下面的Mesh class 添加一个移动赋值运算符,知道 Meshes 的向量作为字段成员出现在我的Model class 中:

    #include <vector>

    struct Mesh {
      std::vector<int> vertexes;
    
      Mesh() {
      }
    
      Mesh& operator=(Mesh&& m) {
        vertexes = std::move(m.vertexes);
    
        return *this;
      }
    };
    
    struct Model {
      std::vector<Mesh> meshes;
    
      Model() {
        meshes.resize(10);
      }
    };
    
    int main() {
      Model model;
    
      return 0;
    }

This code doesn't work though.但是这段代码不起作用。 Below is the error I'm getting (clang was more explicit than gcc):下面是我得到的错误(clang 比 gcc 更明确):

    In file included from script.cpp:1:                                                   
    In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/vector:63:
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:90:7: error: static_assert failed due to requirement 'is_constructib
    le<Mesh, Mesh &&>::value' "result type must be constructible from input type"                                                                                                
          static_assert(is_constructible<_ValueType, _Tp>::value,                                                                                                                
          ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:182:4: note: in instantiation of function template specialization 's
    td::__check_constructible<Mesh, Mesh &&>' requested here                                                                                                                     
            = _GLIBCXX_USE_ASSIGN_FOR_INIT(_ValueType2, _From);                
              ^                                                                           
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:101:13: note: expanded from macro '_GLIBCXX_USE_ASSIGN_FOR_INIT'
        && std::__check_constructible<T, U>()                                                                                                                                    
                ^                                                                         
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:372:19: note: in instantiation of function template specialization '
    std::uninitialized_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
          return std::uninitialized_copy(__first, __last, __result);                                                                                                             
                      ^                                                                   
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:396:19: note: in instantiation of function template specialization '
    std::__uninitialized_copy_a<std::move_iterator<Mesh *>, Mesh *, Mesh>' requested here
          return std::__uninitialized_copy_a                                                                                                                                     
                      ^         
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/vector.tcc:674:14: note: in instantiation of function template specialization 'std::__un
    initialized_move_if_noexcept_a<Mesh *, Mesh *, std::allocator<Mesh>>' requested here
                          std::__uninitialized_move_if_noexcept_a(                                                                                                               
                               ^
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_vector.h:1011:4: note: in instantiation of member function 'std::vector<Mesh>::_M_de
    fault_append' requested here                                                                                                                                                 
              _M_default_append(__new_size - size());
              ^
    script.cpp:20:12: note: in instantiation of member function 'std::vector<Mesh>::resize' requested here
        meshes.resize(10);   
    In file included from script.cpp:1:
    In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/vector:62:
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_construct.h:119:25: error: call to implicitly-deleted copy constructor of 'Mesh'
          ::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
                            ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:120:11: note: in instantiation of function template specialization '
    std::_Construct<Mesh, Mesh>' requested here 
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:137:16: note: in instantiation of function template specialization '
    std::__do_uninit_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
            { return std::__do_uninit_copy(__first, __last, __result); }
                          ^
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:185:2: note: in instantiation of function template specialization 's
    td::__uninitialized_copy<false>::__uninit_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
            __uninit_copy(__first, __last, __result);
            ^
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:372:19: note: in instantiation of function template specialization '
    std::uninitialized_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
          return std::uninitialized_copy(__first, __last, __result);
                      ^
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:396:19: note: in instantiation of function template specialization '
    std::__uninitialized_copy_a<std::move_iterator<Mesh *>, Mesh *, Mesh>' requested here
          return std::__uninitialized_copy_a
                      ^
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/vector.tcc:674:14: note: in instantiation of function template specialization 'std::__un
    initialized_move_if_noexcept_a<Mesh *, Mesh *, std::allocator<Mesh>>' requested here
                          std::__uninitialized_move_if_noexcept_a(
                               ^
    /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_vector.h:1011:4: note: in instantiation of member function 'std::vector<Mesh>::_M_de
    fault_append' requested here
              _M_default_append(__new_size - size());
              ^
    script.cpp:20:12: note: in instantiation of member function 'std::vector<Mesh>::resize' requested here
        meshes.resize(10);
               ^
    script.cpp:9:9: note: copy constructor is implicitly deleted because 'Mesh' has a user-declared move assignment operator
      Mesh& operator=(Mesh&& m) {
        ^

I do realize that:我确实意识到:

  • std::vector::resize() requires that the value_type is default constructible (hence the empty constructor Mesh::Mesh()). std::vector::resize() 要求 value_type 是默认可构造的(因此空构造函数 Mesh::Mesh())。
  • The move assignment operator (like the move constructor) deletes the copy constructor and copy assignment operator.移动赋值运算符(类似于移动构造函数)删除复制构造函数和复制赋值运算符。

My questions are:我的问题是:

  • Why is std::vector::resize() trying to call the deleted copy constructor to allocate space for new items, when an empty constructor is defined?为什么std::vector::resize()在定义空构造函数时尝试调用已删除的复制构造函数来为新项分配空间?

  • Is it failing because one of std::vector::resize() 's requirements isn't met?它是否因为未满足std::vector::resize()要求之一而失败?

  • If so how do I meet it without removing the move assignment?如果是这样,我如何在不删除移动分配的情况下满足它? (if I do my code works, same with resize()). (如果我的代码有效,与 resize() 相同)。

std::vector::resize() requires T to be MoveInsertible and DefaultInsertable . std::vector::resize()要求TMoveInsertibleDefaultInsertable

Per MoveInsertible :MoveInsertible

If A is std::allocator<T> , then this will call placement-new, as by ::new((void*)p) T(rv) (until C++20) std::construct_at(p, rv) (since C++20) .如果Astd::allocator<T> ,则这将调用 placement-new,如::new((void*)p) T(rv) (C++20 前) std::construct_at(p, rv) (C++20 起) This effectively requires T to be move constructible.这实际上要求T是移动可构造的。

If std::allocator<T> or a similar allocator is used, a class does not have to implement a move constructor to satisfy this type requirement: a copy constructor that takes a const T& argument can bind rvalue expressions.如果使用std::allocator<T>或类似的分配器,则class 不必实现移动构造函数来满足此类型要求:采用const T&参数的复制构造函数可以绑定右值表达式。 If a MoveInsertable class implements a move constructor, it may also implement move semantics to take advantage of the fact that the value of rv after construction is unspecified.如果MoveInsertable class 实现移动构造函数,它也可以实现移动语义以利用构造后rv的值未指定的事实。

And per Move constructors :每个Move 构造函数

Implicitly-declared move constructor隐式声明的移动构造函数

If no user-defined move constructors are provided for a class type ( struct , class , or union ), and all of the following is true :如果没有为 class 类型( structclassunion )提供用户定义的移动构造函数,并且以下所有条件都为真

then the compiler will declare a move constructor as a non- explicit inline public member of its class with the signature T::T(T&&) .然后编译器会将移动构造函数声明为其 class 的非显式inline public成员,其签名为T::T(T&&)

Your Mesh class does not have any user-declared copy/move constructors, but it does have a user-declared move assignment operator.您的Mesh class 没有任何用户声明的复制/移动构造函数,但它确实有一个用户声明的移动赋值运算符。 As such, Mesh does not have a compiler-generated move constructor, so it is not move constructible , and thus does not satisfy the requirements of MoveInsertible , hence the compiler error.因此, Mesh没有编译器生成的 move 构造函数,因此它不是move constructible ,因此不满足MoveInsertible的要求,因此编译器错误。

To fix this without removing your move assignment operator, as you requested, you will need to add a user-declared copy constructor and/or move constructor to Mesh , eg:要按照您的要求在删除移动赋值运算符的情况下解决此问题,您需要将用户声明的复制构造函数和/或移动构造函数添加到Mesh ,例如:

struct Mesh {
    std::vector<int> vertexes;
    
    Mesh() {
    }
    
    Mesh(const Mesh& m) : vertexes(m.vertexes) {
    }
    // and/or:
    Mesh(Mesh&& m) : vertexes(std::move(m.vertexes)) {
    }

    // in which case, you may as well consider adding this, too:
    Mesh& operator=(const Mesh& m) {
        if (this != &m) {
            vertexes = m.vertexes;
        }
        return *this;
    }

    Mesh& operator=(Mesh&& m) {
        vertexes = std::move(m.vertexes);
        return *this;
    }
};

However, std::vector is fully copyable and movable by itself, so if you can forget your unnecessary requirement and get rid of the move assignment operator altogether, then all of the compiler-generated constructors and assignment operators will suffice for this example:但是, std::vector本身是完全可复制和移动的,因此如果您可以忘记不必要的要求并完全摆脱移动赋值运算符,那么所有编译器生成的构造函数和赋值运算符都足以满足此示例:

struct Mesh {
    std::vector<int> vertexes;
};

Your class Mesh declares an explicit default constructor, which by default, deletes the move constructor.您的 class Mesh 声明了一个显式默认构造函数,默认情况下会删除移动构造函数。

Either do not declare a constructor, or declare all these 5 functions:要么不声明构造函数,要么声明所有这 5 个函数:

  • default constructor默认构造函数
  • copy constructor复制构造函数
  • move constructor移动构造函数
  • copy operator复制操作员
  • move operator移动操作员

In most cases, but not all, defaults should work fine.在大多数情况下(但不是全部),默认值应该可以正常工作。

Example:例子:

#include <vector>

// Quick explanation of the rules of 0, 3 and 5

struct Mesh_rule_of_0 {
    std::vector<int> vertices;
    // rule of zero.  No explicit constructors, nor operator=() defined.
    // The compiler will generate implicit defaults for you.
};

struct Model_0 {
    std::vector<Mesh_rule_of_0> meshes;

    Model_0() { meshes.resize(10); }
};

struct Mesh_rule_of_3 {
    std::vector<int> vertices;

    // if one of these 3 is defined, then all three must be defined
    // The compiler will _try_ to generate implicit move operations for you.

    Mesh_rule_of_3() = default;
    Mesh_rule_of_3(const Mesh_rule_of_3&) = default;
    Mesh_rule_of_3& operator=(const Mesh_rule_of_3&) = default;
};

struct Model_3 {
    std::vector<Mesh_rule_of_3> meshes;
    Model_3() { meshes.resize(10); }
};

struct Mesh_rule_of_5 {
    std::vector<int> vertices;

    // rule of 5, if a move operator is defined, all 5 must be
    // defined.

    Mesh_rule_of_5() = default;
    Mesh_rule_of_5(const Mesh_rule_of_5&) = default;
    Mesh_rule_of_5(Mesh_rule_of_5&&) = default;

    Mesh_rule_of_5& operator=(const Mesh_rule_of_5&) = default;
    Mesh_rule_of_5& operator=(Mesh_rule_of_5&&) = default;
};

struct Model_5 {
    std::vector<Mesh_rule_of_5> meshes;
    Model_5() { meshes.resize(10); }
};

struct Mesh_fail {
    std::vector<int> vertices;

    // this will fail, because it doesn't satisfy the requirements 
    // of the rule of 0, nor of the rule of 3 nor of 
    // the rule of 5
    Mesh_fail() {}
    Mesh_fail& operator=(Mesh_fail&&) = default;
};

// uncomment for failure
// struct Model_fail {
//     std::vector<Mesh_fail> meshes;
//     Model_fail() { meshes.resize(10); }
// };

int main() {
    Model_0 m0;
    Model_3 m3;
    Model_5 m5;

    // uncomment for failure
    // Model_fail mf;

    return 0;
}

暂无
暂无

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

相关问题 创建 std::vector 时,“结果类型必须可以从输入范围的值类型构造” - "result type must be constructible from value type of input range" when creating a std::vector 尝试压入堆栈时,“结果类型必须可从输入范围的值类型构造”<unique_ptr> (或双端队列)到一个向量 - "result type must be constructible from value type of input range" when trying to push stack<unique_ptr> (or deque) into a vector 收到错误:“错误:static 断言失败:结果类型必须可从输入范围的值类型构造”在构造 class 向量时 - Receiving error: "error: static assertion failed: result type must be constructible from value type of input range" when constructing class vectors 为什么协程的返回类型必须是可移动构造的? - Why must the return type of a coroutine be move-constructible? 为什么要在向量末端放置一个类型T需要移动可构造? - Why should a type T to be emplaced at the end of a vector required to be move constructible? 包括不可移动可构造字段的类型如何可移动可构造? - How could type, comprising non move constructible field, be move constructible? 如果在 co_await 中传递协程返回类型,则它必须是可复制或可移动构造的 - Must coroutine return type be copy- or move-constructible if it is passed in co_await 如何确定类型是否真正可移动构造 - How to get if a type is truly move constructible 应该(在C ++ 11中)std :: vector :: resize(size_type)是否适用于默认的可构造的value_type int [4]? - Should (in C++11) std::vector::resize(size_type) work for the default constructible value_type int[4]? std :: variant vs std :: any当type是move constructible时 - std::variant vs std::any when type is move constructible
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM