C++ 这个奇怪的复制构造函数错误抱怨什么?

C++ 这个奇怪的复制构造函数错误抱怨什么?,c++,c++11,copy-constructor,const-reference,C++,C++11,Copy Constructor,Const Reference,我在Visual Studio 2017上。最近,因为我不喜欢C++的不一致标准,所以我在选项中禁用了非标准语言扩展。到现在为止,一直都还不错。现在我有一个问题 #include <iostream> #include <vector> struct Vertex { Vertex(float pos) { } Vertex(Vertex& other) { } }; std::vector<Vertex> arrayOfVert

我在Visual Studio 2017上。最近,因为我不喜欢C++的不一致标准,所以我在选项中禁用了非标准语言扩展。到现在为止,一直都还不错。现在我有一个问题

#include <iostream>
#include <vector>


struct Vertex
{
    Vertex(float pos) { }
    Vertex(Vertex& other) { }
};

std::vector<Vertex> arrayOfVertices;

int main()
{
    arrayOfVertices.emplace_back(7.f);
}

所以我不知道发生了什么。首先,我想知道,如果没有调用复制构造函数,为什么会发生这种情况,以及在这种情况下,是否有一种方法可以在我的复制构造函数中使用非常量,也就是说,使用
vector::emplace_back()
,因为似乎只有使用
vector::emplace_back()才会出现这个问题

显然,如果编译器给出内部错误,这是一个编译器错误

emplace\u back(7.f)
使用构造函数
Vertex(float pos)
来放置对象——复制构造函数不直接涉及

错误的实际原因是不同的。通常,在向量中放置时,可能会发生重新分配。如果是,则必须将向量中的所有对象重新定位到内存中的新位置

显然,这是一个关于是否发生重新分配的运行时条件。在运行时出现编译错误是不可行的;因此,如果对象不支持重新分配,则在编译时使用
emplace\u back
时必须发生错误;即使此调用的向量恰好为空

标准术语见C++14表87:为了放置到向量中,元素类型必须是可移动且可分配的


无需太多细节,非常量复制构造函数和无移动构造函数的组合意味着对象无法满足MoveInsertable要求,因为所述要求中的右值参数不会绑定到非常量左值引用

问题是您没有移动构造函数

当您请求
std::vector
emplace\u back
某个对象时,它必须确保有足够的存储空间来构建新对象。这个例程的一部分是实例化一组代码,将元素从旧缓冲区移动到任何新分配的缓冲区(如果需要)。该代码将由模板实例化,即使在运行时不会重新分配

您的类有一个用户定义的复制构造函数,因此移动构造函数被隐式删除。因此,通过重载解析,将原始缓冲区中的任何元素移动到新缓冲区的尝试将变成复制的尝试。你对新职位的关注实际上是在转移注意力,真正的问题在这个简单的例子中显而易见:

Vertex v1{7.f},
       v2{std::move(v1)};
       // Error, the xvalue from `move` can't bind to a non-const reference
您可以通过返回move构造函数(例如,通过显式默认)来很容易地消除错误:

struct Vertex
{
    Vertex(float) 
    { 
        std::cout << "Calling constructor\n";
    }

    Vertex(Vertex&&) = default;

    Vertex(Vertex&) 
    { 
        std::cout << "Calling copy constructor\n";
    }
};
struct顶点
{
顶点(浮动)
{ 

std::cout但在通过向量重新分配空间的情况下,必须调用copy/move构造函数…我要说这里发生的一切(可能除了MS接受的非const copy构造函数)有其合理的理由…@在必须重新分配向量的情况下,复制构造函数将使用左值调用,该左值位于旧容器中。请记住,如果复制构造函数可以更改由左值引用的引用旧对象,这将是非常不可取的。请注意,MS的标准库反复声明,
/Za
开关测试不好,标准库并不真正支持它。另一件事是,没有任何理由使用非常量副本构造函数。使用
mutable
类成员可能是更好的解决方案,无论您出于何种动机必须这样做hisSo何时使用R值引用调用我的非常量复制构造函数?何时重新分配?何时重新分配?当重新分配时,它使用复制构造函数移动所有旧容器元素,但这些旧容器元素是左值。哪个是R值?放回的那个?是否重新分配新内存,复制并构建现有的内存结束,然后在新内存的最后一个位置构造新的安置回一个?哦,抱歉,我明白了,它没有复制它们,它是移动构造它们,我缺少移动构造函数。奇怪的是,错误消息没有提到这一点。@Zebrafish-不,在重新分配时它试图显式移动它们。默认情况下不复制。上覆oad resolution可以在接受常量引用时调用复制构造函数作为回退。但在您的情况下,根本没有可行的重载。@Zebrafish-MSVC有一个实现错误。这很不幸,但最终与您的代码无关。请记住,如果您将类与
vector
一起使用,最好使用re移动构造函数是
noexcept
,否则它将尝试使用复制构造函数,如果该构造函数不可用,则您将失去对向量异常安全性的保证。(默认移动构造函数通常应隐式为
noexcept
)哪个是重新分配时的r值?所有旧容器元素都是l值,最后一个新元素的emplace_back参数被发送到普通构造函数,否?@Zebrafish请参阅我链接的MoveInsertable的描述。rvalue是用于指定MoveInsertable要求的假设值:包含的类型必须可以从右值构造。您可能会发现一个典型的向量实现相当于在旧元素上执行std::move以移动到新位置(使用一些额外的胡言乱语来处理非noexcept move构造函数)
struct Vertex
{
    Vertex(float) 
    { 
        std::cout << "Calling constructor\n";
    }

    Vertex(Vertex&&) = default;

    Vertex(Vertex&) 
    { 
        std::cout << "Calling copy constructor\n";
    }
};