C ++标准是否规定了编译器的STL实现细节? [英] Does the C++ standard specify STL implementation details for the compiler?

查看:331
本文介绍了C ++标准是否规定了编译器的STL实现细节?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在撰写问题的答案时我面临一个有趣的情况 - 这个问题演示了一个场景,其中一个人想要在STL容器中放置一个类,但由于缺少副本构造函数/移动构造函数/赋值运算符而无法这样做。在这种特殊情况下,错误由 std :: vector :: resize 触发。我做了一个快速的代码片段作为解决方案,并看到另一个答案,提供了一个移动构造函数,而不是一个赋值运算符和复制构造函数,因为我有。什么是interresting,另一个答案没有编译在VS 2012,而clang / gcc对这两种方法很满意。



第一:

  // Clang和gcc对此感到满意,VS 2012不是
#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 / VS2012都很满意这个
#include< memory>
#include< vector>

using namespace std;
class FooImpl {};

class Foo
{
unique_ptr< FooImpl> myImpl;
public:
Foo()
{
}
〜Foo()
{
}
Foo(const Foo& foo)
{
//用指针做什么?
}
Foo& operator =(const Foo& foo)
{
if(this!=& foo)
{
//
}
return * this;
}
};

int main(int argc,char ** argv)
{
vector< Foo> testVec;
testVec.resize(10);
return 0;
}

要了解发生了什么,我在VS 2012中查看了STL源代码,它真的是调用移动赋值运算符,这就是为什么我的示例工作(我没有可以访问的Linux机器,以了解clang / gcc中发生了什么),另一个没有,因为它只有移动副本因此这创建了以下问题 - 编译器可以自由决定如何实现STL方法(在这种情况下 std :: vector ::

resize ),因为根本不同的实现可能导致非便携式代码?

解决方案

最重要的是,由于c ++ 11, std :: vector<> 可以存储不可复制的类型。 (示例)让我们来看看 cppreference



直到c ++ 11,T应该可以复制。 >


T必须满足CopyAssignable和CopyConstructible的要求。


但是,在c ++ 11中,需求完全改变。


对元素的要求取决于实际对容器执行的操作。一般来说,元素类型必须是完整类型并且符合可擦除的要求,但许多成员函数会强加更严格的要求。


.. 可擦除是:


如果



A 分配器类型定义为 X :: allocator_type



m X获取的类型 A :: get_allocator()



p 由容器



准备的 T * 类型下面的表达式格式正确:

  std :: allocator_traits< A> :: destroy(m,p); 


看看 std :: vector :: resize()参考


T必须符合 MoveInsertable的要求 DefaultInsertable ,以便使用overload(1)。 p>

所以T不需要是可复制的 - 它只需要可破坏,可移动和默认可构造。



此外,由于c ++ 14,完全类型的限制被删除。


施加在元素上取决于对容器执行的实际操作。一般来说,要求元素类型满足可擦除的要求,但是许多成员函数提出了更严格的要求。 如果分配器满足分配器完整性要求,则此容器(而不是其成员)可以使用不完整的元素类型进行实例化。


因此,我认为这是因为VS2012的标准一致性差。它在最新的C ++上有一些缺陷(例如 noexcept






< C ++ 11标准文件 N3337


void resize(size_type sz);



E ff ects:If sz <= size(),等效于 erase(begin()+ sz,end()); 。如果 size()< sz ,将
sz - size()附加到序列中。



需要:T必须是CopyInsertable into * this。


因此在严格的c ++ 11中, code> std :: vector :: resize()。 (您可以使用 std :: vector



但是,这是一个标准缺陷,并在C ++ 14中修复。并且我想许多编译器与非可复制类型工作良好,因为复制不需要实现 std :: vector :: resize()确实。虽然VS2012不工作,这是因为VS2012的另一个错误作为@ComicSansMS回答,而不是因为 std :: vector :: resize()本身。


While writing an answer to this question I faced an interesting situation - the question demonstrates the scenario where one would want to put a class in an STL container but fails to do so because of a missing copy constructor/move constructor/assignment operator. In this particular case the error is triggered by std::vector::resize. I made a quick snippet as a solution and saw another answer that had provided a move constructor instead of an assignment operator and copy constructor as I had. What was interresting that the other answer did not compile in VS 2012, while clang/gcc were happy with both approaches.

First:

// 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;
}

Second:

// 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;
}

To understand what was happening I looked at the STL sources in VS 2012 and saw that it really was invoking the move assignment operator so that is why my sample worked (I don't have a Linux machine accessible to understand what is going on in clang/gcc) and the other did not, since it had only the move copy constructor.

So this created the following question - can the compiler freely decide how to implement STL methods (in this case std::vector::resize), since radically different implementations could cause non-portable code? Or is this simply a VS 2012 bug?

解决方案

Above all, since c++11, std::vector<> can store not-copyable types. (example) Let's take a look at cppreference.

Until c++11, T should be copyable as you know.

T must meet the requirements of CopyAssignable and CopyConstructible.

However, in c++11, The requirements is completely changed.

The requirements that are imposed on the elements depend on the actual operations performed on the container. Generally, it is required that element type is a complete type and meets the requirements of Erasable, but many member functions impose stricter requirements.

.. Erasable is:

The type T is Erasable from the Container X if, given

A the allocator type defined as X::allocator_type

m the lvalue of type A obtained from X::get_allocator()

p the pointer of type T* prepared by the container

the following expression is well-formed:

std::allocator_traits<A>::destroy(m, p);

And look at the "Type requirements" of std::vector::resize() reference:

T must meet the requirements of MoveInsertable and DefaultInsertable in order to use overload (1).

So T doesn't need to be copyable - it only needs to destroyable, movable and default construct-able.

Moreover, since c++14, the restriction of complete type is removed.

The requirements that are imposed on the elements depend on the actual operations performed on the container. Generally, it is required that element type meets the requirements of Erasable, but many member functions impose stricter requirements. This container (but not its members) can be instantiated with an incomplete element type if the allocator satisfies the allocator completeness requirements.

Therefore, I think it's because of just poor standard-conforming of VS2012. It has some defect on latest C++ (e.g. noexcept)


C++11 standard paper N3337 says

void resize(size_type sz);

Effects: If sz <= size(), equivalent to erase(begin() + sz, end());. If size() < sz, appends sz - size() value-initialized elements to the sequence.

Requires: T shall be CopyInsertable into *this.

Therefore in strict c++11, you cannot use std::vector::resize() in this case. (you can use std::vector, though)

However, it is a standard defect and fixed in C++14. and I guess many compilers work well with non-copyable types because copying isn't need to implementing std::vector::resize() indeed. Although VS2012 doesn't work, it's because another bug of VS2012 as @ComicSansMS answered, not because of std::vector::resize() itself.

这篇关于C ++标准是否规定了编译器的STL实现细节?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆