Reputation: 53163
So, this isn't really something I have to do, I was just playing around. I wrote a Vector
class for vectors of any numeric type and any number of coordinates. It is used as Vector<NumericType, [num of coords]>
. Here is the code:
#include <array>
#include <functional>
namespace World {
template <typename NumType, unsigned char Size>
class Vector
{
public:
using CoordType = NumType;
template<typename... NumTypes>
constexpr Vector(NumTypes&&... vals) : values{ std::forward<NumTypes>(vals)... }
{
static_assert(sizeof...(NumTypes) == Size, "You must provide N arguments.");
}
Vector(const std::array<NumType, Size>& values) : values(values) {}
Vector(const std::array<NumType, Size>&& values) : values(std::move(values)) {}
const NumType& operator[](size_t offset) const { return values[offset]; }
NumType& operator[](size_t offset) { return values[offset]; }
//! Converts all values to new given type
template <typename NewType>
constexpr Vector<NewType, Size> Convert() const { return Convert<NewType>(std::make_index_sequence<Size>{}); }
//! Converts all values via the conversion function
template <typename NewType>
Vector<NewType, Size> Convert(const std::function<NewType(NumType)>& callback) const { return Convert<NewType>(std::make_index_sequence<Size>{}, callback); }
std::array<NumType, Size> values;
private:
//! Converts all values to new given type
template <typename NewType, std::size_t ... Is>
constexpr Vector<NewType, Size> Convert(std::index_sequence<Is...>) const { return { { static_cast<NewType>(values[Is])}... }; }
//! Converts all values via the conversion function
template <typename NewType, std::size_t ... Is>
Vector<NewType, Size> Convert(std::index_sequence<Is...>, const std::function<NewType(NumType)>& callback) const { return { { callback(values[Is])}... } ; }
};
Now what I am trying to do now is to make sure that the conversion function declared above is working. The idea of usage is this:
using namespace World;
using Vector3D = Vector<double, 3>;
using Vector3Int = Vector<int, 3>;
#include <cmath>
int main()
{
const Vector3D src{ 1.4, 2.5, 3.6 };
const Vector3Int target = src.Convert<int>([](double val) { return (int)std::round(val); });
return 0;
}
The issue here is that when the conversion function is compiled, the resulting new values appear in the form of std::initializer_list<NewType>
. For some reason, this doesn't qualify for the constructor Vector(NumTypes&&... vals)
. Now I do not want to have an initializer list constructor - the number of expected arguments is not variable, it must be whatever the Size
template parameter says.
So how to get around this? How can I convert std::initializer_list
to whatever NumTypes&&... vals
is?
I must admit here I do not precisely know what I'm doing, I am trying to improve my C++ knowledge.
Upvotes: 3
Views: 376
Reputation: 117308
In Convert
, you have the parameter pack Is
which you'd like to use in conjunction with callback
and values
.
Parameter pack expansion "expands to comma-separated list of zero or more patterns", so this is what you'll get when used with the values in your question:
Is...
0, 1, 2
values[Is]...
=> values[0], values[1], values[2]
1.4, 2.5, 3.6
callback(values[Is])...
=> callback(values[0]), callback(values[1]), callback(values[2])
1, 3, 4
And you want to create a braced-init-list like this:
{callback(values[0]), callback(values[1]), callback(values[2])}
// =>
{1, 3, 4}
So:
template <typename NewType, std::size_t... Is>
Vector<NewType, Size>
Convert(std::index_sequence<Is...>,
const std::function<NewType(NumType)>& callback) const
{
return {callback(values[Is])...};
}
Upvotes: 1