David Hall
David Hall

Reputation: 535

Construct std::set from array

Why doesn't C++ provide us with a constructor which takes an array as an argument? Alternatively, is there anything wrong with defining the following function?

template <class T>
std::set<T> ArrayToSet(T array[]) {
  return std::set<T>(array, array + sizeof(array) / sizeof(array[0]));
}

I think the answer might come down to ideas of dynamic memory allocation, but I'd like to hear some feedback on this.

Edit: I am not using C++11.

Upvotes: 3

Views: 4647

Answers (3)

Zac Howland
Zac Howland

Reputation: 15872

Why doesn't C++ provide us with a constructor which takes an array as an argument?

Why would it? A std::set is not an array, and it already has a constructor that takes iterators to initialize it, so adding another constructor for an array is unnecessary. std::vector IS an array and even it does not have a constructor that takes an array.

Alternatively, is there anything wrong with defining the following function?

Yes and no. It is unnecessary as you can just write

MyType myArray[mySize];
std::set<MyType> mySet(myArray, myArray + sizeof(myArray) / sizeof(array[0]);
// or std::set<MyType> mySet(myArray, myArray + mySize);
// or std::set<MyType> mySet(std::begin(myArray), std::end(myArray)); c++11

It isn't really worthy of its own function.

If you really want to write a function to help you out, I'd approach it by porting std::begin and std::end to C++03. Those would at least be more usable than a function specifically to create a std::set.

It would look exactly like what Konrad posted in his answer.

Upvotes: 5

Konrad Rudolph
Konrad Rudolph

Reputation: 545528

Alternatively, is there anything wrong with defining the following function?

There is: it doesn’t work.

You cannot pass variable-length C-style arrays to functions. The T array[] syntax in an argument list is a synonym for T* array: a raw pointer is passed, not an argument.

You can, however, pass fixed-sized arrays by reference (i.e. T (&array)[5] works). To make this work for different array lengths you need to use a non-type template argument:

template <class T, std::size_t N>
std::set<T> ArrayToSet(T (&array)[N]) {
  return std::set<T>(array, array + N);
}

– But I agree in this with Zac: the function is over-specific (or, the other way round: not generic enough). There is already a universal collection-construction method, via iterators. So the correct way is to use C++11’s std::begin and std::end facility, and if you cannot use C++11, then the correct way is to write them yourself:

template <typename T, std::size_t N>
T* begin(T (&array)[N]) {
    return array;
}

template <typename T, std::size_t N>
T* end(T (&array)[N]) {
    return array + N;
}

Upvotes: 6

nikolas
nikolas

Reputation: 8975

std::set does not need to provide a constructor for C style arrays, because it is possible to construct from them already using iterators - furthermore it is not easy to construct from an array, because you could try to construct from T *, which does not convey the array length, or whether it's an array at all.

To do this, we use a template trick to determine the array size and use the iterator constructor:

template <typename T, size_t N>
std::set<T> ArrayToSet(T (& array)[N]) {
    return std::set<T>(&array[0], &array[0]+N);
}

Note that, as stated in the comments, this will not work for T * types. You could overload for that giving an additional parameter arr_length or something. In this case sizeof doesn't work either, it would just give you the size of the pointer, not the array.

Upvotes: 3

Related Questions