2

For a small software renderer project I want to work on I'd need different types of vectors so I thought I'd template them up.

template<typename T, size_t dim> struct Vector {
  std::array<T, dim> data;

  Vector(){
    data = { 0 };
  }
}

This works nice with empty vectors like:

Vector<int, 3> v;

But how can I create a constructor that would accept a sytax like this:

Vector<int, 3> v(1, 2, 3);

Thought an std::initializer_list could work like this:

Vector(std::initializer_list<T> values){
  data = values;
}
Vector<int, 3> v({1, 2, 3});

But the compiler says there's no acceptable conversion between std::array and std::initializer_list and the ({1, 2, 3}) syntax looks kinda clunky too.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
val
  • 729
  • 6
  • 19
  • Write a constructor from `std::initializer_list`. – Violet Giraffe Oct 06 '16 at 16:25
  • Possibly relevant: http://stackoverflow.com/questions/8192185/using-stdarray-with-initialization-lists – mindriot Oct 06 '16 at 16:42
  • 2
    You can take `std::array` as parameter constructor instead of `std::initializer_list`, So you have the size guaranty. – Jarod42 Oct 06 '16 at 17:02
  • @Jarod42 That actually works with the ({}) syntax. On a side question: does this creating/passing an std::array have any serious performace overhead? I'll be throwing in/out/around these vectors quite a bit i feel. – val Oct 06 '16 at 17:11
  • 1
    With correct implementation, you will have `dim` `move` (which is copy for `int`/`float`). `std::initializer` would force the copy. And passing directly by arguments would also require move/copy. So I don't see overhead against the alternatives. If you want a `Vector` cheaper to move, `std::vector` might be an alternative to `std::array` (for the member). – Jarod42 Oct 06 '16 at 17:28
  • `std::array` doesn't have a constructor from `initializer_list`, as I've just learned (that sucks). But yes, you can pass an `std::array` instead an init that from aggregate (same curly braces syntax, different semantics). – Violet Giraffe Oct 06 '16 at 17:52

2 Answers2

5

You can use variadic template:

template <typename ... Ts>
Vector(Ts&&... args) : data{{std::forward<Ts>(args)...}}
{}

With potentially some SFINAE to restrict this constructor to good number of args, and args convertible to T.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • With a c++14 compliant compiler one `{}` should be enough, right? – Bob__ Oct 06 '16 at 16:52
  • 1
    @Bob__: `std::array` is a strange beast.... with only `data{args...}`, compiler allows it but clang warns about missing brace (-Wmissing-braces). – Jarod42 Oct 06 '16 at 16:58
1

It won't work with std::initializer_list, but it will with std::array proxy:

#include <array>
#include <iostream>

template<typename T, size_t dim> struct Vector {
  std::array<T, dim> data;

  Vector(){
    data = { 0 };
  }

  Vector(std::array<T, dim> initial_values) : data(initial_values) {}

};

int main() {
    Vector<int, 3> v({1, 2, 3});
}
Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
  • Could you show an example how to call it? Cant compile it with either v(1, 2, 3) or v({1, 2, 3}). – val Oct 06 '16 at 16:33
  • Not as good,than it should be, clang warns about missing brace: [Demo](http://coliru.stacked-crooked.com/a/1ae2b27afaa297f4). (but if you add them, then you have ambiguous call :-/ [Demo](http://coliru.stacked-crooked.com/a/c09233b3cfb3adf0)). You can still use brace syntax constructor instead of parents, but it seems ugly to me [Demo](http://coliru.stacked-crooked.com/a/ac30ebf5c3eaf531). – Jarod42 Oct 07 '16 at 21:52