C++ C++;是否为编译器指定标准STL实现细节?
在回答问题时,我遇到了一个有趣的情况——这个问题演示了这样一种场景:一个人想要将一个类放入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
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,编译器仍然是一个