Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/152.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 向量::推回和标准::移动_C++_C++11_Move_Rvalue - Fatal编程技术网

C++ 向量::推回和标准::移动

C++ 向量::推回和标准::移动,c++,c++11,move,rvalue,C++,C++11,Move,Rvalue,我尝试了以下代码: #include <iostream> struct test{ test(){} test(test const &){ std::cout<<__LINE__<<"\n"; } test(test&&){ std::cout<<__LINE__<<"\n"; } }; #include <vector>

我尝试了以下代码:

#include <iostream>

struct test{
    test(){}
    test(test const &){
        std::cout<<__LINE__<<"\n";
    }
    test(test&&){
        std::cout<<__LINE__<<"\n";
    }
};

#include <vector>
#include <utility> // std::move
int main(){
    auto&& tmp = test();
    std::vector<test> v;
    v.push_back(tmp);
    std::cout<<__LINE__<<"\n";
    v.push_back(std::move(tmp));

    return 0;
}
#包括
结构测试{
test(){}
测试(测试常数&){
标准::cout
是
tmp
test&
的类型吗

是和否。
tmp
是类型为
test&
的右值参考变量,但作为表达式的标识符
tmp
具有类型
test
和值类别左值。
&
决不是表达式类型的一部分

tmp
是右值吗

否。标识符的任何使用都是一个左值表达式,甚至是右值引用的名称。右值引用变量的访问方式基本上与左值引用变量相同;只有
decltype(tmp)
可以区分差异。(通常您会使用
decltype((tmp))
来避免区分差异。)

如果tmp的类型是test&,为什么第一个push_back没有使用move构造函数

因为右值引用的名称仍然是左值。若要获取右值表达式,请使用
move(tmp)

最后的输出来自哪里?为什么vs2013和g++输出不同的结果

默认情况下,Clang和GCC只为
向量中的一个对象腾出空间。添加第二个对象时,向量存储被重新分配,导致对象被复制。为什么它们没有被移动?因为移动构造函数不是
noexcept
,因此如果它引发异常,则无法撤消重新分配。

至于MSVC的两个动作,有两种可能,你可以通过实验来区分——我手边没有副本

  • 默认情况下,MSVC在向量中为两个对象保留了足够的空间。第二步是从内部局部变量移动
  • MSVC忽略了move构造函数必须是
    noexcept
    的要求,并调用它来执行重新定位。这可能是一个bug,但在这种情况下,它掩盖了一个常见错误

  • 如果将
    vector
    替换为
    deque
    ,您将看不到更多副本,因为
    deque
    不允许假定可复制性。

    Visual Studio尚不支持noexcept关键字,并且可能不符合push_back的异常安全性。此外,额外的输出是cap上的差异造成的人口增长计算

    #include <iostream>
    #include <vector>
    #include <utility> // std::move
    
    struct Except{
        Except(){}
        Except(Except const &) {
            std::cout<< "COPY\n";
        }
        Except(Except&&)  {
            std::cout<< "MOVE\n";
        }
    };
    
    struct NoExcept{
        NoExcept(){}
        NoExcept(NoExcept const &) noexcept {
            std::cout<< "COPY\n";
        }
        NoExcept(NoExcept&&) noexcept {
            std::cout<< "MOVE\n";
        }
    };
    
    template <typename T> void Test( char const *title,int reserve = 0) {
        auto&& tmp = T();
        std::cout<< title <<"\n";
        std::vector<T> v;
        v.reserve(reserve);
    
        std::cout<< "LVALUE REF ";
        v.push_back(tmp);
        std::cout<< "RVALUE REF ";
        v.push_back(std::move(tmp));
        std::cout<< "---\n\n";
    }
    int main(){
        Test<Except>( "Except class without reserve" );
        Test<Except>( "Except class with reserve", 10 );
        Test<NoExcept>( "NoExcept class without reserve" );
        Test<NoExcept>( "NoExcept class with reserve", 10 );
    }
    

    基本正确,但对构造函数的额外调用只是因为向量正在重新定位,而不是使用“临时内部变量”就在向量创建之后。这里不是有一些引用崩溃的诡计吗,例如,
    tmp
    是RHS返回的吗?它与整个“通用引用”是一样的。所以test&&与test&?@andrew.punnett没有优势。谢谢,我只是假设GCC和Clang保留了足够的空间。(
    test
    应该只有一个字节,动态分配少于几个字的内容很少有好处。)aa而且,MSVC可能出现错误。典型。@user1535111取决于您所说的“优势”是什么意思。右值引用和左值引用之间的最大区别在于它们的初始化方式。尝试替换
    auto&
    ,看看会发生什么。我已经修改了我的答案以扩展Andrew的评论。最有效的解决方案是
    v.emplace_back();
    ,它既不需要复制也不需要移动。注意:
    v.emplace_back()
    如果对象是在call@DarkWanderer
    v.emplace_back();
    直接在向量的内存中构造对象。在调用之前没有创建任何对象。@FredOverflow:Nope。
    MyClass实例;v.emplace_back(实例);
    。虽然我移动的想法是错误的,但它将通过复制构造函数进行复制。
    Except class without reserve
    LVALUE REF COPY
    RVALUE REF MOVE
    COPY
    ---
    
    Except class with reserve
    LVALUE REF COPY
    RVALUE REF MOVE
    ---
    
    NoExcept class without reserve
    LVALUE REF COPY
    RVALUE REF MOVE
    MOVE
    ---
    
    NoExcept class with reserve
    LVALUE REF COPY
    RVALUE REF MOVE
    ---