93

Is the absence of

std::array<T,size>::array(const T& value);

an oversight? It seems mighty useful to me, and dynamic containers (like std::vector) do have a similar constructor.

I am fully aware of

std::array<T,size>::fill(const T& value);

but that is not a constructor, and the memory will be zeroed out first. What if I want all -1's like this guy?

5
  • 1
    "and the memory will be zeroed out first" are you sure this is true? Commented Jul 29, 2013 at 12:14
  • 3
    It won't be zeroed out first, unless you ask for it. Commented Jul 29, 2013 at 12:14
  • 1
    Besides the aggregate-argument from all the answers, there could also be a more conceptual reasoning. A fill-constructor would probably hide the fact that it isn't really constructing the individual elements. It will first and foremost invoke the aggregate initialization and then copy the value into the elements, it can't copy-construct the elements right away (in contrast to, say a std::vector). So since it would always be equivalent to array(); array.fill();, omitting the constructor in the first place doesn't hide this fact. Commented Jul 29, 2013 at 12:38
  • 1
    Also relevant: stackoverflow.com/questions/18497122/… Commented Sep 2, 2013 at 15:52
  • Unfortunately GCC range designated initialization isn't implemented in C++. gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html Maybe then we could write std::array<int, 10> a = { [0 ... 9] = 1}. Commented Feb 1 at 18:23

5 Answers 5

61

std::array is, by design, an aggregate, so has no user-declared constructors.

As you say, you could use fill after default constructing. Since it's an aggregate, default construction won't zero the memory, but will leave it uninitialised (if the contained type is trivially initialisable).

Sign up to request clarification or add additional context in comments.

5 Comments

So this page is wrong? It says explicitly that the array's elements are default-initialized.
Default-initialization is no-init for PODs, and default-constructor for everything else I believe - depending on the point of declaration.
@rubenvb: Yes, they'll be default-initialised, not value-initialised. So if they are trivially initialisable, then they'll be left uninitialied.
Ah yes. That still remains a very naughty part of the standard IMO which draws a distinction between user and built-in types :/
@rubenvb The primitive types all have trivial default-initialization. User-defined types can behave in the same way if desired. That's not distinction, it's consistency.
25

Note that you can efficiently simulate this type of constructor by taking advantage of the fact that array is not zero-initialized, and has a copy constructor and do.

template <size_t N, class T>
array<T,N> make_array(const T &v) {
    array<T,N> ret;
    ret.fill(v);
    return ret;
}

auto a = make_array<20>('z');

9 Comments

let's not bloat the call site with unneeded template parameters ;-).
char can be inferred, so you can just write make_array<20>('z') instead of make_array<20,char>('z')
@Nawaz oh, even better. I should have asked why there is no make_array instead :-)
@Walter: You couldn't return a reference of any sort, because you'd return a reference to a local variable.
This isn't going to work when T is not default constructible, and that's when you need the fill constructor badly.
|
18

You may use std::index sequence for that:

namespace detail
{

    template <typename T, std::size_t...Is>
    constexpr std::array<T, sizeof...(Is)>
    make_array(const T& value, std::index_sequence<Is...>)
    {
        return {{(static_cast<void>(Is), value)...}};
    }
}

template <std::size_t N, typename T>
constexpr std::array<T, N> make_array(const T& value)
{
    return detail::make_array(value, std::make_index_sequence<N>());
}

Demo

std::make_index_sequence is C++14, but can be implemented in C++11.

static_cast<void>(Is) is to handle evil operator, that T might provide.

6 Comments

This is the most useful answer, it works also when T is not default constructible, which is when the other answers fall short.
@ChrisBeck Could not agree more. That was precisely the case for me and I was getting frustrated with the other answers. Goes to show that up-votes ain't everything.
Because of using template recursion, this won't compile for large arrays. If I need a million-element array of T with no default constructor am I out of luck? Even a C array would work for me.
@MichałBrzozowski: std::index_sequence might be implemented with logarithm instantiation instead of linear. and compiler might have "intrinsics" to do only one instantiation. Then, there are no longer recursion.
@MichałBrzozowski: You can still switch to std::vector, and reserve whole size and emplace_back in a loop.As for a million-element, stack would be problematic as memory limited in practice.
|
11

First of all, it is not std::array<T>, it is std::array<T,N> where N is compile time constant integral expression.

Second, std::array is made aggregate by design. So it doesn't have anything which makes it non-aggregate, which is why it doesn't have constructor... and destructor, virtual functions, etc.

Comments

2

I took answer from Jarod42 and made an extension to be able to use variable amount of Constructor arguments and also added automatic indexer as a first arg:

namespace detail {
  template <typename T, std::size_t... Seq, typename... Args>
  constexpr std::array<T, sizeof...(Seq)> make_array(std::index_sequence<Seq...>, Args &... args)
  {
    return {{(static_cast<void>(Seq), T(Seq, args...))...}};
  }
}  // namespace detail

template <typename T, std::size_t N, typename... Args>
constexpr std::array<T, N> make_array(Args &... args)
{
  return detail::make_array<T>(std::make_index_sequence<N>(), args...);
}

class myClass {
  myClass(unsigned int i, float a, std::string b, int c):... {};
}

Usage:

auto myArray = make_array<myClass, 64>(a, b, c);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.