In my Point header I have:
15 template<typename real> class Point
16 {
17 public:
18 // Constructors
19 Point();
20 Point(const std::initializer_list<real>&);
21 Point(const std::initializer_list<real>&, const types::unitTypes);
22 Point(const real, const real, const real);
23 Point(const real, const real, const real, const types::unitTypes);
...
43 private:
44 std::array<real, 3> xyz_;
45 types::unitTypes units_;
46 };
Note that lines 20 and 44 show that the Point
to should be able to be initialized with an initializer_list
and I have private variable std::array<real, 3> xyz_
. Now, I would like for my constructor of this to be something like the following:
31 template<typename T>
32 Point<T>::Point(const std::initializer_list<T>& xyz)
33 : xyz_(xyz)
34 , units_(types::au)
35 {};
However, it doesn't seem like I'm able to construct the array
from an initializer
and if I try to move that modify that from :xyz_(xyz)
to something like
31 template<typename T>
32 Point<T>::Point(std::initializer_list<T> xyz)
33 : units_(types::au)
34 {
35 xyz_ = xyz;
36 };
it is not able to overload =
for operands array
and initializer
. Is there a better way to go about this that I can use invoke Point<real>({x, y, z});
and initialize the xyz_
array internally?
Update:
I had tried to define Point(const std::array<real, 3>&)
before but I get the following compilation error (essential parts extracted):
error: call of overloaded ‘Point(<brace-enclosed initializer list>)’ is ambiguous
...
note: candidate: ‘aided::point::Point<real>::Point(const std::array<real, 3>&) [with real = float]’
...
note: candidate: ‘constexpr aided::point::Point<float>::Point(const aided::point::Point<float>&)’
...
note: candidate: ‘constexpr aided::point::Point<float>::Point(aided::point::Point<float>&&)’
The first is candidate is the one I am intending to use. Are the second two somehow copy constructors that are able to be invoked via an initialization with an initializer list?
CodePudding user response:
std::initializer_list
and std::array
don’t cooperate as well as one would hope. Separate constructor arguments can give you a bit more flexibility, automatic template argument deduction and (also, to some extent) automatic choice of a type that can hold all the values:
#include <array>
#include <iostream>
#include <type_traits>
#include <utility>
template <typename Real, size_t N>
struct Point {
template <typename... R>
Point(R &&...reals) : xyz_{static_cast<Real>(std::forward<R>(reals))...} {}
private:
std::array<Real, N> xyz_;
template <size_t Head, size_t... Tail>
void print(std::ostream &out, std::index_sequence<Head, Tail...>) const {
out << xyz_[Head];
((out << ',' << xyz_[Tail]), ...);
}
friend std::ostream &operator<<(std::ostream &out, const Point &point) {
out << typeid(Real).name() << ' ' << '[';
point.print(out, std::make_index_sequence<N>());
return out << ']';
}
};
template <typename... R>
Point(R &&...) -> Point<std::common_type_t<R...>, sizeof...(R)>;
Now let’s test that↑ a bit and let’s not insist on Real
too strongly:
#include <complex>
#include "that_magic_point.h"
int main() {
Point p0{1, 2, 3}; // int
Point p1{4., 5., 6.}; // double
Point p2{7, 8., 9}; // int, double -> double
Point p3{10, 11, 12ll}; // int, long long -> long long
Point p4{1}; // int
Point p5{2., 3.}; // double
Point p6{4, 5., 6u, 7}; // int, double, unsigned -> double
Point p7{std::complex{1, 2}, 3}; // complex<int>, int -> complex<int>
Point p8{4, std::complex{5., 6.}}; // int, complex<double> -> complex<double>
// Caveat: This resolves (incorrectly) to complex<int>:
// Point p9{std::complex{7, 8}, 9.};
// Caveat: This will not compile (cast from complex<int> to complex<double>):
// Point<std::complex<double>, 2> p9{std::complex{7, 8}, 9.};
// Caveat: This is verbose:
Point<std::complex<double>, 2> p9{std::complex<double>{7, 8}, 9.};
std::cout << p0 << '\n'
<< p1 << '\n'
<< p2 << '\n'
<< p3 << '\n'
<< p4 << '\n'
<< p5 << '\n'
<< p6 << '\n'
<< p7 << '\n'
<< p8 << '\n'
<< p9 << '\n';
}
This↑ seems to work and may generate the following output (modulo compilers’ RTTI naming differences):
i [1,2,3]
d [4,5,6]
d [7,8,9]
x [10,11,12]
i [1]
d [2,3]
d [4,5,6,7]
St7complexIiE [(1,2),(3,0)]
St7complexIdE [(4,0),(5,6)]
St7complexIdE [(7,8),(9,0)]
Solving some of the caveats outlined in comments using deep(er) template decomposition and specialization would be a nice exercise, but may not be worth the hassle.