C++ C++;是否为编译器指定标准STL实现细节?

C++ C++;是否为编译器指定标准STL实现细节?,c++,visual-c++,gcc,stl,clang,C++,Visual C++,Gcc,Stl,Clang,在回答问题时,我遇到了一个有趣的情况——这个问题演示了这样一种场景:一个人想要将一个类放入STL容器中,但由于缺少复制构造函数/移动构造函数/赋值操作符而未能这样做。在这种特殊情况下,错误由std::vector::resize触发。我制作了一个快速片段作为解决方案,并看到了另一个答案,它提供了一个移动构造函数,而不是像我一样提供赋值运算符和复制构造函数。另一个答案没有在VS 2012中编译,而clang/gcc对这两种方法都很满意 第一: // Clang and gcc are happy

在回答问题时,我遇到了一个有趣的情况——这个问题演示了这样一种场景:一个人想要将一个类放入STL容器中,但由于缺少复制构造函数/移动构造函数/赋值操作符而未能这样做。在这种特殊情况下,错误由
std::vector::resize
触发。我制作了一个快速片段作为解决方案,并看到了另一个答案,它提供了一个移动构造函数,而不是像我一样提供赋值运算符和复制构造函数。另一个答案没有在VS 2012中编译,而clang/gcc对这两种方法都很满意

第一:

// Clang and gcc are happy with this one, VS 2012 is not
#include <memory>
#include <vector>

class FooImpl {};

class Foo
{
    std::unique_ptr<FooImpl> myImpl;
public:
    Foo( Foo&& f ) : myImpl( std::move( f.myImpl ) ) {}
    Foo(){}
    ~Foo(){}
};

int main() {
    std::vector<Foo> testVec;
    testVec.resize(10);
    return 0;
}
//Clang和gcc对此感到满意,VS 2012则不然
#包括
#包括
类FooImpl{};
福班
{
std::unique_ptr myImpl;
公众:
Foo(Foo&&f):myImpl(std::move(f.myImpl)){}
Foo(){}
~Foo(){}
};
int main(){
std::向量testVec;
testVec.resize(10);
返回0;
}
第二:

// Clang/gcc/VS2012 are all happy with this
#include <memory>
#include <vector>

using namespace std;
class FooImpl {};

class Foo
{
    unique_ptr<FooImpl> myImpl;
public:
    Foo()
    {
    }
    ~Foo()
    {
    }
    Foo(const Foo& foo)
    {
        // What to do with the pointer?
    }
    Foo& operator= (const Foo& foo)
    {
        if (this != &foo)
        {
            // What to do with the pointer?
        }
        return *this;
    }
};

int main(int argc, char** argv)
{
    vector<Foo> testVec;
    testVec.resize(10);
    return 0;
}
//Clang/gcc/VS2012对此都很满意
#包括
#包括
使用名称空间std;
类FooImpl{};
福班
{
独特的_ptrmyimpl;
公众:
Foo()
{
}
~Foo()
{
}
Foo(const Foo&Foo)
{
//如何处理指针?
}
Foo&operator=(常量Foo&Foo)
{
如果(此!=&foo)
{
//如何处理指针?
}
归还*这个;
}
};
int main(int argc,字符**argv)
{
向量testVec;
testVec.resize(10);
返回0;
}
为了了解发生了什么,我查看了VS 2012中的STL源代码,发现它确实在调用move赋值操作符,这就是为什么我的示例有效(我没有一台Linux机器可以访问以了解clang/gcc中的情况),而另一台没有,因为它只有move-copy构造函数


因此,这就产生了一个问题——编译器能否自由决定如何实现STL方法(在本例中是
std::vector::resize
),因为完全不同的实现可能会导致代码不可移植?或者,这仅仅是VS 2012的bug吗?< /P> < P> C++标准对几乎所有库容器函数都规定了<代码> T <代码>的约束。

例如,在草案n4296中,[vector.capacity]/13中定义的
T
对于
std::vector::resize
的约束如下:

Requires: T shall be MoveInsertable and DefaultInsertable into *this.

<>我没有访问C++版本的最终标准来进行比较,但是我假设VS 2012在它的C++ 11支持中是不符合的。在本文中,

< P>首先,因为C++ 11,<代码> STD::vector < /COD> <强>可以< /St>存储不可复制类型。(让我们看一下。

在c++11之前,T应该是可复制的

T必须满足可复制分配和可复制构造的要求

然而,在c++11中,需求完全改变了

对构件施加的要求取决于对容器执行的实际操作。通常,要求元素类型为完整类型,并满足可擦除的要求,但许多成员函数的要求更为严格

。。是:

如果给定,类型T可以从容器X中擦除

A
定义为
X::allocator\u type

m
类型
A
的左值,从
X::get\u分配器()

p
容器准备的
T*
类型指针

以下表达式格式正确:

std::allocator_traits<A>::destroy(m, p);
std::分配器特性:

T必须满足和的要求才能使用过载(1)

所以T不需要是可复制的——它只需要是可销毁的、可移动的和可默认构造的

此外,自c++14以来,完全类型的限制被删除

对构件施加的要求取决于对容器执行的实际操作。通常,要求元素类型满足可擦除的要求,但许多成员函数要求更严格如果分配器满足分配器完整性要求,则可以使用不完整的元素类型实例化此容器(但不是其成员)

因此,我认为这是因为VS2012的标准符合性差。在最新的C++中(如<>代码>除< /代码>)


C++11标准文件说

调整空隙大小(尺寸_类型sz)


<代码>:如果代码> SZ

Visual C++ 2012是不可能的。只能修复的缺陷

您可以通过向
Foo
添加显式移动赋值运算符来编译第一个示例:

#include <memory>
#include <vector>

class FooImpl {};

class Foo
{
    std::unique_ptr<FooImpl> myImpl;
public:
    Foo( Foo&& f ) : myImpl( std::move( f.myImpl ) ) {}
    // this function was missing before:
    Foo& operator=( Foo&& f) { myImpl = std::move(f.myImpl); return *this; }
    Foo(){}
    ~Foo(){}
};

int main() {
    std::vector<Foo> testVec;
    testVec.resize(10);
    return 0;
}
#包括
#包括
类FooImpl{};
福班
{
std::unique_ptr myImpl;
公众:
Foo(Foo&&f):myImpl(std::move(f.myImpl)){}
//此功能在以下情况之前丢失:
Foo&operator=(Foo&f){myImpl=std::move(f.myImpl);返回*this;}
Foo(){}
~Foo(){}
};
int main(){
std::向量testVec;
testVec.resize(10);
返回0;
}

正如中详细解释的,标准实际上不需要移动分配操作符。
vector::resize()
的相关概念是和,您最初使用move构造函数就可以满足这些概念

VC的实现在这里也需要移动分配,这是另一个缺陷,在VS2013中已经修复


由于其对这一问题的深刻贡献,

VS2012是C++编译器,具有C++ 11个特性。称它为C++11编译器有点牵强

它的标准库是C++03。它对移动语义的支持很少

到了VS2015,编译器仍然是一个