I'm trying to better understand templates and for this purpose I made an educational struct:
template<typename T, size_t N>
struct SVectorN
{
SVectorN(const T(&another)[N]);
private:
T components[N];
};
Constructor that I made allows me to make an instance, like this:
double input[4] = { 0.1, 0.2, 0.3, 0.4 };
SVectorN<double, 4> t(input);
But I can't figure out how to support initializating like this:
SVectorN<double, 4> c = { 1.3, 1.4, 1.5, 1.6 };
Or even better something like this:
SVectorN c(1.3, 1.4, 1.5, 1.6 ); // SVectorN<double, 4>
Is it possible or am I missing something comepletely?
Thanks
Both approaches are possible, initialization in C++ is tricky simply because of how many ways there are and their subtle differences.
I would recommend something like this:
#include <cstdint>
#include <utility>
template <typename T, std::size_t N> struct SVectorN {
SVectorN(const T (&another)[N]);
template <typename First, typename Second, typename... Tail>
SVectorN(First &&f, Second &&s, Tail &&...t)
: components{std::forward<First>(f), std::forward<Second>(s),
std::forward<Tail>(t)...} {}
private:
T components[N];
};
int main() {
double input[4] = {0.1, 0.2, 0.3, 0.4};
SVectorN<double, 4> t(input);
SVectorN<double, 4> c = {1.3, 1.4, 1.5, 1.6};
SVectorN<double, 4> d{1.3, 1.4, 1.5, 1.6};
SVectorN<double, 4> e(1.3, 1.4, 1.5, 1.6);
}
I had to explicitly enforce presence of at least two arguments in the variadic ctor, otherwise it would be a better overload match for t
's construction and that would fail.
c,d,e
are practically the same initialization if there is not ctor with std::initializer_list
. Which there is not because initializing an array with one cannot be easily done.
Now, if you want to automatically deduce the template parameters, that is possible thanks to CTAD . To simplify that link, you have to just specify to the compiler how to deduce class's template arguments from a constructor's arguments.
For example the following:
template <class T, std::size_t N>
explicit SVectorN(const T (&)[N]) -> SVectorN<T, N>;
template <typename F, typename S, typename... Tail>
SVectorN(F &&, S &&, Tail &&...) -> SVectorN<F, sizeof...(Tail) + 2>;
will allow:
SVectorN tx(input);
SVectorN cx = {1.3, 1.4, 1.5, 1.6};
SVectorN dx{1.3, 1.4, 1.5, 1.6};
SVectorN ex(1.3, 1.4, 1.5, 1.6);
I really like the answer from @Quimby, but I'm not sure if it would work with one argument. Alternatively, you could overload the constructors with std::initializer_list
and variadic
. It's not very efficient at this stage, but you can optimize it as per your needs.
#include <iostream>
template <typename T, size_t N>
struct SVectorN
{
private:
T components[N];
public:
SVectorN(const T(&another)[N])
{
std::copy(std::begin(another), std::end(another), components);
}
SVectorN(std::initializer_list<T> &&init_)
{
std::copy(std::begin(init_), std::end(init_), components);
}
template <typename ... U>
SVectorN(U ...val_)
{
T tmp[] = { val_ ... };
std::copy(std::begin(tmp), std::end(tmp), components);
}
void print() const
{
for (auto &&c : components)
std::cout<< c <<",";
std::cout<<std::endl;
}
};
int main()
{
double input[4] = { 0.1, 0.2, 0.3, 0.4 };
SVectorN<double, 4> t(input);
t.print();
SVectorN<double, 4> c = { 1.3, 1.4, 1.5, 1.6 };
c.print();
SVectorN<double, 4> d(1.3, 1.4, 1.5, 1.6 );
d.print();
}
Results are:
0.1,0.2,0.3,0.4,
1.3,1.4,1.5,1.6,
1.3,1.4,1.5,1.6,
Online code: https://rextester.com/MIF22368
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.