STL Container constructed from other container (e.g. list from vector)

In my code, sometimes I need to construct a container from another container. But these container objects are of different types, so I can't use a copy constructor. What I do looks like this (C++11, I use uniform initialization and constructor delegation) :

std::vector<int> source {1, 3, 5, 7, 9};
std::list<int> destination (source.begin (), source.end ());

I realized that std::list could have a constructor like this, to make things prettier:

template </* ... */>
class list
{
public:
    template <class Container> list (const Container& container)
    : list (container.begin (), container.end ())
    {
    }

    /* ... */
};

Unfortunately I can do this only for my own classes, not for STL classes. So first question, of there a reason such costructors don't exist in STL and Boost? And second question, is there a safe way to implement such a ctor for STL containers, maybe by defining conversion operators somehow?

This issue os of course just a matter of convenience, nothing critical to code or to program behavior. But it could make the code look a bit more readable:

std::vector<int> source {1, 3, 5, 7, 9};
std::list<int> destination = source;

Upvotes: 4

Views: 2135

Answers (3)

Kai Petzke
Kai Petzke

Reputation: 2944

I think the problem is, that letting a container be instantiated from anything iterable leads to a lot of unexpected behaviour and ambiguities. For example:

auto mystring = std::string { "ABC" };
auto mylist = std::list { mystring };

Shall mylist be instantiated here as a list of strings with just one element (as is currently the case with C++17), or as a list of the three characters 'A', 'B' and 'C', as would be the case in the iterable unrolling? Making the unrolling explicit solves that problem neatly, even, if it is a bit tedious to write.

Upvotes: 0

Germ&#225;n Diago
Germ&#225;n Diago

Reputation: 7663

There is a proposal in c++14 that, if I'm not wrong, would enable to pass another container as a "traversable" directly to a container's constructor.

The latest revision of the proposal can be found here.

Upvotes: 0

joshuanapoli
joshuanapoli

Reputation: 2509

You should be suspicious of code where the complete range of one container type is copied to another. This is an uncommon activity.

In C++, explicit conversions are usually preferred. Implicit conversions and assignment operators that convert from one type to another are usually discouraged because they sometimes lead to unexpected conversions.

If you truly need to frequently convert between container types, try defining an explicit helper function for the conversion:

template<typename ToType, typename FromType>
ToType container_cast(const FromType& source)
{
    return ToType(source.begin(), source.end(), source.get_allocator());
}

Then your example becomes:

std::vector<int> source {1, 3, 5, 7, 9};
auto destination = container_cast<std::list<int> >(source);

Upvotes: 7

Related Questions