Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the C++ standard specify STL implementation details for the compiler?

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?

like image 888
Rudolfs Bundulis Avatar asked Jun 19 '15 10:06

Rudolfs Bundulis


People also ask

Does C language have STL?

Standard C++ includes the standard library commonly known as the STL. Every implementation of standard C++ includes the standard library.

What is STL implementation?

Standard Template Library (STL) The Standard Template Library (STL), part of the C++ Standard Library, offers collections of algorithms, containers, iterators, and other fundamental components, implemented as templates, classes, and functions essential to extend functionality and standardization to C++.

What is the Standard Template Library STL and the benefits of using the STL in data structures?

Standard Template Library is the latest edition in C++. STL provides programmers to store the data effectively, and do manipulation in stored data. These are the general-purpose templates of classes and functions that help in implementing the basic algorithms and data structures like vector, lists, queue, stack, etc.

Is STL in the C++ standard?

Sometimes "STL" is also used to refer to the containers and algorithms parts of the C++ Standard Library adapted from Stepanov's STL. In this documentation, Standard Template Library (STL) refers to the C++ Standard Library as a whole.


1 Answers

Visual C++ 2012 is unable to auto-generate the move constructor and the move assignment operator. A defect that will only be fixed in the upcoming 2015 version.

You can make your first example compile by adding an explicit move assignment operator to 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;
}

As explained in detail by ikh's answer, the standard actually does not require a move assignment operator here. The relevant concepts for vector<T>::resize() are MoveInsertable and DefaultInsertable, which would have been met by your initial implementation with just the move constructor.

The fact that VC's implementation also requires the move assignment here is a different defect, which was already fixed in VS2013.

Thanks to ikh and dyp for their insightful contributions in this matter.

like image 94
ComicSansMS Avatar answered Oct 01 '22 11:10

ComicSansMS