Initialise `std::array` with parameter pack arguments - c++

There's structure HasArray with template parameters typename T and size_t N.
template<typename T, size_t N>
struct HasArray {
// enable_if sizeof...(T) equals N
template<typename ... Types, typename std::enable_if<sizeof...(Types) == N, int>::type = 0>
explicit HasArray(Types ... s) : arr { s... } {}
protected:
std::array<T, N> arr;
};
I would like to initialize the member array arr with the parameter-pack arguments of the constructor.
HasArray<uint32_t, 2> foo(7, 13);
But that yields a c++11-narrowing warning in Clang.
error: non-constant-expression cannot be narrowed from type 'int' to 'std::__1::array<unsigned int, 2>::value_type' (aka 'unsigned int') in initializer list [-Wc++11-narrowing]
I see no way to cast all s's of type Types to type T. Is there one?
Edit thanks for all answers. I ended up using static_cast<> on the packed parameters and SFINAE when not convertible:
template<typename T, size_t N>
struct HasArray {
// Use `static_cast` to construct `arr` with `s`
// Add `enable_if` all `Types` are `is_convertible`
template<typename ... Types,
typename std::enable_if<sizeof...(Types) == N, int>::type = 0,
typename std::enable_if<(std::is_convertible<Types, T>::value && ...), int>::type = 0>
explicit HasArray(Types ... s) : arr { static_cast<T>(s)... } {}
protected:
std::array<T, N> arr;
};

If you want to construct std::array from any value convertible to T, then the easiest solution would be just to add static_cast<T>(...) in your constructor.
template<typename T, size_t N>
struct HasArray {
template<typename ... Types,
typename std::enable_if<sizeof...(Types) == N, int>::type = 0>
explicit HasArray(Types ... s) : arr {static_cast<T>(s)... } {}
protected:
std::array<T, N> arr;
};
https://godbolt.org/z/TEoZOG
It's also possible to "SFINAE out" the constructor in case such conversion is not possible, but In my opinion the default error message would be better in current simple case and you could add static_asserts with better message in constructor body.

You might use intermediate class:
template <std::size_t, typename T>
using always_t = T;
template <typename T, typename Seq>
struct HasArrayImpl;
template <typename T, std::size_t...Is>
struct HasArrayImpl<T, std::index_sequence<Is...>>
{
explicit HasArrayImpl(always_t<Is, T> ... v) : arr { v... } {}
protected:
std::array<T, sizeof...(Is)> arr;
};
template <typename T, std::size_t N>
using HasArray = HasArrayImpl<T, std::make_index_sequence<N>>;
Else, you can extend your SFINAE to convertible type and explicitly convert values
template<typename T, size_t N>
struct HasArray {
// enable_if sizeof...(T) equals N
template <typename ... Types,
std::enable_if_t<(sizeof...(Types) == N)
&& (std::is_convertible<Types, T>::value && ...),
int>::type = 0>
explicit HasArray(Types ... s) : arr{ static_cast<T>(s)... } {}
protected:
std::array<T, N> arr;
};

I see no way to enforce that all types Types must be of type T. Is there one?
I don't understand, from your question if you want that Types are deducible template types, and all of they are deduced exactly as T, or if you want a no-template constructor that receive exaclty N values of type T.
The first case is simple (if you can use C++17 template folding; a little more complicated otherwise) because you can use std::is_same
template <typename ... Types,
typename std::enable_if<(sizeof...(Types) == N)
&& (... && std::is_same<Types, T>::value), int>::type = 0>
explicit HasArray(Types ... s) : arr {{ s... }}
{ }
For second case I propose a variation of the Jarod42 solution that uses a specialization of HasArray instead of a intermediate class (edit: added an improvement from Jarod42 itself; thanks!):
template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct HasArray;
template<typename T, std::size_t N, std::size_t ... Is>
struct HasArray<T, N, std::index_sequence<Is...>>
{
static_assert( sizeof...(Is) == N , "wrong sequence size" );
protected:
std::array<T, N> arr;
public:
explicit HasArray(getType<T, Is> ... s) : arr {{ s... }}
{ }
};
where getType is
template <typename T, std::size_t>
using getType = T;
In first case,
HasArray<std::uint32_t, 2> foo(7, 13);
gives compilation error because 7 and 13 are deduced as int that isn't std::uin32_t.
In second case it compile becase HasArray has a constructor
HasArray(std::uint32_t, std::uint32_t)
and the ints are converted to std::uint32_t.
The following is a full compiling C++14 example for the second case
#include <array>
#include <cstdint>
#include <type_traits>
template <typename T, std::size_t>
using getType = T;
template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct HasArray;
template<typename T, std::size_t N, std::size_t ... Is>
struct HasArray<T, N, std::index_sequence<Is...>>
{
static_assert( sizeof...(Is) == N , "wrong sequence size" );
protected:
std::array<T, N> arr;
public:
explicit HasArray(getType<T, Is> ... s) : arr {{ s... }}
{ }
};
int main ()
{
HasArray<std::uint32_t, 2> foo(7, 13);
}

Related

How to declare std::array when element type has no default constructor? [duplicate]

How do I initialize std::array<T, n> if T is not default constructible?
I know it's possible to initialize it like that:
T t{args};
std::array<T, 5> a{t, t, t, t, t};
But n for me is template parameter:
template<typename T, int N>
void f(T value)
{
std::array<T, N> items = ???
}
And even if it wasn't template, it's quite ugly to repeat value by hand if n is too large.
Given N, you could generate a sequence-type calledseq<0,1,2,3,...N-1> using a generator called genseq_t<>, then do this:
template<typename T, int N>
void f(T value)
{
//genseq_t<N> is seq<0,1,...N-1>
std::array<T, N> items = repeat(value, genseq_t<N>{});
}
where repeat is defined as:
template<typename T, int...N>
auto repeat(T value, seq<N...>) -> std::array<T, sizeof...(N)>
{
//unpack N, repeating `value` sizeof...(N) times
//note that (X, value) evaluates to value
return {(N, value)...};
}
And the rest is defined as:
template<int ... N>
struct seq
{
using type = seq<N...>;
static const std::size_t size = sizeof ... (N);
template<int I>
struct push_back : seq<N..., I> {};
};
template<int N>
struct genseq : genseq<N-1>::type::template push_back<N-1> {};
template<>
struct genseq<0> : seq<> {};
template<int N>
using genseq_t = typename genseq<N>::type;
Online demo
Hope that helps.
Sadly the existing answers here don't work for non-copyable types. So I took #Nawaz answer and modified it:
#include <utility>
#include <array>
template<typename T, size_t...Ix, typename... Args>
std::array<T, sizeof...(Ix)> repeat(std::index_sequence<Ix...>, Args &&... args) {
return {{((void)Ix, T(args...))...}};
}
template<typename T, size_t N>
class initialized_array: public std::array<T, N> {
public:
template<typename... Args>
initialized_array(Args &&... args)
: std::array<T, N>(repeat<T>(std::make_index_sequence<N>(), std::forward<Args>(args)...)) {}
};
Note that this is an std::array subclass so that one can easily write
class A {
A(int, char) {}
}
...
class C {
initialized_array<A, 5> data;
...
C(): data(1, 'a') {}
}
Without repeating the type and size. Of course, this way can also be implemented as a function initialize_array.
Following will solve your issue:
#if 1 // Not in C++11, but in C++1y (with a non linear better version)
template <std::size_t ...> struct index_sequence {};
template <std::size_t I, std::size_t ...Is>
struct make_index_sequence : make_index_sequence<I - 1, I - 1, Is...> {};
template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
#endif
namespace detail
{
template <typename T, std::size_t ... Is>
constexpr std::array<T, sizeof...(Is)>
create_array(T value, index_sequence<Is...>)
{
// cast Is to void to remove the warning: unused value
return {{(static_cast<void>(Is), value)...}};
}
}
template <std::size_t N, typename T>
constexpr std::array<T, N> create_array(const T& value)
{
return detail::create_array(value, make_index_sequence<N>());
}
So test it:
struct NoDefaultConstructible {
constexpr NoDefaultConstructible(int i) : m_i(i) { }
int m_i;
};
int main()
{
constexpr auto ar1 = create_array<10>(NoDefaultConstructible(42));
constexpr std::array<NoDefaultConstructible, 10> ar2 = create_array<10>(NoDefaultConstructible(42));
return 0;
}
Inspired by #Nawaz, this is more concise and makes better use of the standard: https://godbolt.org/z/d6a7eq8T5
#include <array>
#include <type_traits>
#include <fmt/format.h>
#include <fmt/ranges.h>
// Inspired by https://stackoverflow.com/a/18497366/874660
//! Return a std::array<T, Sz> filled with
//! calls to fn(i) for i in the integer sequence:
template <typename Int, Int...N, typename Fn>
[[nodiscard]] constexpr auto transform_to_array(std::integer_sequence<Int, N...>, Fn fn) {
return std::array{(static_cast<void>(N), fn(N))...};
}
//! Repeated application of nullary fn:
template <std::size_t N, typename Fn>
[[nodiscard]] constexpr auto generate_n_to_array(Fn fn) -> std::array<decltype(fn()), N> {
return transform_to_array(std::make_integer_sequence<std::size_t, N>(), [&](std::size_t) { return fn(); });
}
int main() {
static constexpr std::array<int, 3> a = generate_n_to_array<3>([i = 0]() mutable { return 2 * (i++); });
fmt::print("{}\n", a);
}
There are already many great answers, so a single remark:
most of them use N twice: once in the array<T,N> and once in the repeat/genseq/generate/whatever you call it...
Since we are using a single value anyway, it seems we can avoid repeating the size twice, here's how:
#include <iostream>
#include <array>
#include <type_traits>
// Fill with a value -- won't work if apart from non-default-constructible, the type is also non-copyable...
template <typename T>
struct fill_with {
T fill;
constexpr fill_with(T value) : fill{value} {}
template <typename... Args>
constexpr fill_with(std::in_place_type_t<T>, Args&&... args) : fill{ T{std::forward<Args>(args)...} } {}
template <typename U, size_t N>
constexpr operator std::array<U, N> () {
return [&]<size_t... Is>(std::index_sequence<Is...>) {
return std::array{ ((void)Is, fill)... };
}(std::make_index_sequence<N>{});
}
};
// A little more generic, but requires C++17 deduction guides, otherwise using it with lambdas is a bit tedious
template <typename Generator>
struct construct_with {
Generator gen;
template <typename F> constexpr construct_with(F && f) : gen{std::forward<F>(f)} {}
template <typename U, size_t N>
constexpr operator std::array<U, N> () {
return [&]<size_t... Is>(std::index_sequence<Is...>) {
return std::array{ ((void)Is, gen())... };
}(std::make_index_sequence<N>{});
}
};
template <typename F>
construct_with(F&&) -> construct_with<F>;
struct A {
A(int){}
A(A const&) = delete;
};
int main() {
std::array<int, 7> arr = fill_with<int>{ 7 };
std::array<int, 7> arr = fill_with{ 7 }; // Ok since C++17
// or you can create a maker function to wrap it in pre-c++17 code, as usual.
std::array<A, 7> as = construct_with{[]{ return A{7}; }};
for (auto & elem : arr) std::cout << elem << '\n';
}
Note: Here I used a few C++20 constructs, which may or may not be an issue in your case, but changing from one to the other is pretty straight-forward
I'd also recommend using <type_traits> and std::index_sequence and all the other sequences from the std if possible to avoid reinventing the wheel)

getting a value for any type in a constexpr environment without default constructibility [duplicate]

Is there a utility in the standard library to get the index of a given type in std::variant? Or should I make one for myself? That is, I want to get the index of B in std::variant<A, B, C> and have that return 1.
There is std::variant_alternative for the opposite operation. Of course, there could be many same types on std::variant's list, so this operation is not a bijection, but it isn't a problem for me (I can have first occurrence of type on list, or unique types on std::variant list).
Update a few years later: My answer here may be a cool answer, but this is the correct one. That is how I would solve this problem today.
We could take advantage of the fact that index() almost already does the right thing.
We can't arbitrarily create instances of various types - we wouldn't know how to do it, and arbitrary types might not be literal types. But we can create instances of specific types that we know about:
template <typename> struct tag { }; // <== this one IS literal
template <typename T, typename V>
struct get_index;
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: std::integral_constant<size_t, std::variant<tag<Ts>...>(tag<T>()).index()>
{ };
That is, to find the index of B in variant<A, B, C> we construct a variant<tag<A>, tag<B>, tag<C>> with a tag<B> and find its index.
This only works with distinct types.
I found this answer for tuple and slightly modificated it:
template<typename VariantType, typename T, std::size_t index = 0>
constexpr std::size_t variant_index() {
static_assert(std::variant_size_v<VariantType> > index, "Type not found in variant");
if constexpr (index == std::variant_size_v<VariantType>) {
return index;
} else if constexpr (std::is_same_v<std::variant_alternative_t<index, VariantType>, T>) {
return index;
} else {
return variant_index<VariantType, T, index + 1>();
}
}
It works for me, but now I'm curious how to do it in old way without constexpr if, as a structure.
You can also do this with a fold expression:
template <typename T, typename... Ts>
constexpr size_t get_index(std::variant<Ts...> const&) {
size_t r = 0;
auto test = [&](bool b){
if (!b) ++r;
return b;
};
(test(std::is_same_v<T,Ts>) || ...);
return r;
}
The fold expression stops the first time we match a type, at which point we stop incrementing r. This works even with duplicate types. If a type is not found, the size is returned. This could be easily changed to not return in this case if that's preferable, since missing return in a constexpr function is ill-formed.
If you dont want to take an instance of variant, the argument here could instead be a tag<variant<Ts...>>.
With Boost.Mp11 this is a short, one-liner:
template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
Full example:
#include <variant>
#include <boost/mp11/algorithm.hpp>
using namespace boost::mp11;
template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
int main()
{
using V = std::variant<int,double, char, double>;
static_assert(IndexInVariant<V, int> == 0);
// for duplicates first idx is returned
static_assert(IndexInVariant<V, double> == 1);
static_assert(IndexInVariant<V, char> == 2);
// not found returns ".end()"/ or size of variant
static_assert(IndexInVariant<V, float> == 4);
// beware that const and volatile and ref are not stripped
static_assert(IndexInVariant<V, int&> == 4);
static_assert(IndexInVariant<V, const int> == 4);
static_assert(IndexInVariant<V, volatile int> == 4);
}
One fun way to do this is to take your variant<Ts...> and turn it into a custom class hierarchy that all implement a particular static member function with a different result that you can query.
In other words, given variant<A, B, C>, create a hierarchy that looks like:
struct base_A {
static integral_constant<int, 0> get(tag<A>);
};
struct base_B {
static integral_constant<int, 1> get(tag<B>);
};
struct base_C {
static integral_constant<int, 2> get(tag<C>);
};
struct getter : base_A, base_B, base_C {
using base_A::get, base_B::get, base_C::get;
};
And then, decltype(getter::get(tag<T>())) is the index (or doesn't compile). Hopefully that makes sense.
In real code, the above becomes:
template <typename T> struct tag { };
template <std::size_t I, typename T>
struct base {
static std::integral_constant<size_t, I> get(tag<T>);
};
template <typename S, typename... Ts>
struct getter_impl;
template <std::size_t... Is, typename... Ts>
struct getter_impl<std::index_sequence<Is...>, Ts...>
: base<Is, Ts>...
{
using base<Is, Ts>::get...;
};
template <typename... Ts>
struct getter : getter_impl<std::index_sequence_for<Ts...>, Ts...>
{ };
And once you establish a getter, actually using it is much more straightforward:
template <typename T, typename V>
struct get_index;
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: decltype(getter<Ts...>::get(tag<T>()))
{ };
That only works in the case where the types are distinct. If you need it to work with independent types, then the best you can do is probably a linear search?
template <typename T, typename>
struct get_index;
template <size_t I, typename... Ts>
struct get_index_impl
{ };
template <size_t I, typename T, typename... Ts>
struct get_index_impl<I, T, T, Ts...>
: std::integral_constant<size_t, I>
{ };
template <size_t I, typename T, typename U, typename... Ts>
struct get_index_impl<I, T, U, Ts...>
: get_index_impl<I+1, T, Ts...>
{ };
template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
: get_index_impl<0, T, Ts...>
{ };
My two cents solutions:
template <typename T, typename... Ts>
constexpr std::size_t variant_index_impl(std::variant<Ts...>**)
{
std::size_t i = 0; ((!std::is_same_v<T, Ts> && ++i) && ...); return i;
}
template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T>(static_cast<V**>(nullptr));
template <typename T, typename V, std::size_t... Is>
constexpr std::size_t variant_index_impl(std::index_sequence<Is...>)
{
return ((std::is_same_v<T, std::variant_alternative_t<Is, V>> * Is) + ...);
}
template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T, V>(std::make_index_sequence<std::variant_size_v<V>>{});
If you wish a hard error on lookups of not containing type or duplicate type - here are static asserts:
constexpr auto occurrences = (std::is_same_v<T, Ts> + ...);
static_assert(occurrences != 0, "The variant cannot have the type");
static_assert(occurrences <= 1, "The variant has duplicates of the type");
Another take on it:
#include <type_traits>
namespace detail {
struct count_index {
std::size_t value = 0;
bool found = false;
template <typename T, typename U>
constexpr count_index operator+(const std::is_same<T, U> &rhs)
{
if (found)
return *this;
return { value + !rhs, rhs};
}
};
}
template <typename Seq, typename T>
struct index_of;
template <template <typename...> typename Seq, typename... Ts, typename T>
struct index_of<Seq<Ts...>, T>: std::integral_constant<std::size_t, (detail::count_index{} + ... + std::is_same<T, Ts>{}).value> {
static_assert(index_of::value < sizeof...(Ts), "Sequence doesn't contain the type");
};
And then:
#include <variant>
struct A{};
struct B{};
struct C{};
using V = std::variant<A, B, C>;
static_assert(index_of<V, B>::value == 1);
Or:
static_assert(index_of<std::tuple<int, float, bool>, float>::value == 1);
See on godbolt: https://godbolt.org/z/7ob6veWGr

Expand a type N times in template parameter

I have the following problem:
template< std::size_t N >
class A
{
std::function< std::size_t( /*std::size_t,....,std::size_t <- N-times*/) > foo;
};
As you can see above, I try to declare an std::function<...> foo as a member of a class A. Here, I want foo to have the return type std::size_t (which is no problem) and as input, I will pass N-times the type std::size_t but I don't know how. Is there any possibility?
Many thanks in advance.
You can use std::index_sequence:
template<std::size_t N, typename = std::make_index_sequence<N>>
struct A;
template<std::size_t N, std::size_t... S>
struct A<N, std::index_sequence<S...>> {
std::function<std::size_t(decltype(S)...)> foo;
};
Live example
If you like, you could also define to what type it expands:
template<typename T, std::size_t N, typename = std::make_index_sequence<N>>
struct A;
template<typename T, std::size_t N, std::size_t... S>
struct A<T, N, std::index_sequence<S...>> {
template<std::size_t>
using type = T;
std::function<std::size_t(type<S>...)> foo;
};
For arbitrary type and not just size_t, just write a helper alias:
template<class T, size_t>
using Type = T;
template<std::size_t... S>
struct AHelper<std::index_sequence<S...>> {
std::function<size_t(Type<MyArbitraryTypeHere, S>...)> foo;
};
Ok this was fun. Here is my solution:
namespace details {
template <size_t N, class F = size_t()>
struct Function_type_helper {};
template <size_t N, class... Args>
struct Function_type_helper<N, size_t(Args...)> {
using Type = typename Function_type_helper<N - 1, size_t(Args..., size_t)>::Type;
};
template <class... Args>
struct Function_type_helper<0, size_t(Args...)> {
using Type = size_t(Args...);
};
template <size_t N, class F = size_t()>
using Function_type_helper_t = typename Function_type_helper<N, F>::Type;
static_assert(std::is_same_v<Function_type_helper_t<3>, size_t(size_t, size_t, size_t)>);
} // ns details
template<size_t N>
struct A
{
std::function<details::Function_type_helper_t<N>> foo;
};
This works by recursively creating the type size_t(size_t, size_t, ..., size_t)
For instance:
H<3>::Type == H<3, size_t()>::Type ==
H<2, size_t(size_t)>::Type ==
H<1, size_t(size_t, size_t)>::Type ==
H<0, size_t(size_t, size_t, size_t)>::Type ==
size_t(size_t, size_t, size_t)

Preventing conversion operators from compiling unless a static condition is met

I have Vector (CVector<T, std::size_t Size>), Matrix (CMatrix<T, std::size_t Height, std::size_t Width>) and Tensor (CTensor<T, std::size_t... Sizes>) classes, and I wish to be able to implicitly convert from a CTensor class to a CVector class if sizeof...(Sizes) == 1 and to a CMatrix class if sizeof...(Sizes) == 2, so I have the following conversion operators (initially I didn't have the std::enable_if template parameter hoping I could use SFINAE to prevent it from compiling):
template <typename std::enable_if<sizeof...(Sizes) == 2, int>::type = 0>
operator CMatrix<NumType, Sizes...>() const
{
static_assert(sizeof...(Sizes) == 2, "You can only convert a rank 2 tensor to a matrix");
CMatrix<NumType, Sizes...> matResult;
auto& arrThis = m_numArray;
auto& arrResult = matResult.m_numArray;
concurrency::parallel_for_each( arrResult.extent, [=, &arrThis, &arrResult]( concurrency::index<2> index ) restrict( amp ) {
arrResult[index] = arrThis[index];
} );
return matResult;
}
template <typename std::enable_if<sizeof...(Sizes) == 1, int>::type = 0>
operator CVector<NumType, Sizes...>() const
{
static_assert(sizeof...(Sizes) == 1, "You can only convert a rank 1 tensor to a vector");
CVector<NumType, Sizes...> vecResult;
auto& arrThis = m_numArray;
auto& arrResult = vecResult.m_numArray;
concurrency::parallel_for_each( arrResult.extent, [=, &arrThis, &arrResult]( concurrency::index<1> index ) restrict( amp ) {
arrResult[index] = arrThis[index];
} );
return vecResult;
}
However, if I instantiate CTensor<float, 3, 3, 3> for instance, and try to compile, I will be presented with errors declaring that there are too many template parameters for CMatrix and CVector along with errors regarding missing type for std::enable_if<false, int>. Is there a way to implement these operators without having to specialize CTensor for rank 1 and 2?
I have simplified my previous solution, details below.
SFINAE is not needed at all because you have static_assert in template method which is instantiated only upon usage.
My solution makes the conversion operator a template method with dependant argument (so that compiler does not instantiate its body, only parses signature), and adds -1 size that pretends to be missing dimension within tensor of size 1 (not to the tensor itself, but to helper class that extracts parameter pack), to allow compiler instantiate the tensor template itself, but will not allow later on to instantiate the conversion operator within tensor of invalid dimension.
Live demo link.
#include <cstddef>
template <typename T, unsigned int index, T In, T... args>
struct GetArg
{
static const T value = GetArg<T, index-1, args...>::value;
};
template <typename T, T In, T... args>
struct GetArg<T, 0, In, args...>
{
static const T value = In;
};
template <typename T, T In>
struct GetArg<T, 1, In>
{
static const T value = -1;
};
template <typename T, std::size_t Size>
struct CVector
{
};
template <typename T, std::size_t Height, std::size_t Width>
struct CMatrix
{
};
template <typename T, std::size_t... Sizes>
struct CTensor
{
template <std::size_t SZ = sizeof...(Sizes)>
operator CVector<T, GetArg<std::size_t, 0, Sizes...>::value>() const
{
static_assert(SZ == 1, "You can only convert a rank 1 tensor to a vector");
CVector<T, Sizes...> vecResult;
return vecResult;
}
template <std::size_t SZ = sizeof...(Sizes)>
operator CMatrix<T, GetArg<std::size_t, 0, Sizes...>::value, GetArg<std::size_t, 1, Sizes...>::value>() const
{
static_assert(SZ == 2, "You can only convert a rank 2 tensor to a matrix");
CMatrix<T, Sizes...> matResult;
return matResult;
}
};
int main()
{
CTensor<float, 3> tensor3;
CTensor<float, 3, 3> tensor3_3;
CTensor<float, 3, 3, 3> tensor3_3_3;
CVector<float, 3> vec(tensor3);
//CVector<float, 3> vec2(tensor3_3); // static_assert fails!
CMatrix<float, 3, 3> mat(tensor3_3);
//CMatrix<float, 3, 3> mat2(tensor3_3_3); // static_assert fails!
}
Here is how you could do it with static_assert:
template <typename NumType,size_t... Sizes>
struct CTensor {
template<size_t n,size_t m>
operator CMatrix<NumType,n,m>() const
{
static_assert(
sizeof...(Sizes)==2,
"You can only convert a rank 2 tensor to a matrix"
);
static_assert(
std::is_same<CTensor<NumType,n,m>,CTensor>::value,
"Size mismatch"
);
...
}
template<size_t n>
operator CVector<NumType,n>() const
{
static_assert(
sizeof...(Sizes)==1,
"You can only convert a rank 1 tensor to a vector"
);
static_assert(
std::is_same<CTensor<NumType,n>,CTensor>::value,
"Size mismatch"
);
...
}
};
or with SFINAE:
template <typename NumType,size_t... Sizes>
struct CTensor {
template<size_t n,size_t m,
typename =
typename std::enable_if<
std::is_same<CTensor<NumType,n,m>,CTensor>::value, int
>::type
>
operator CMatrix<NumType,n,m>() const
{
...
}
template<size_t n,
typename =
typename std::enable_if<
std::is_same<CTensor<NumType,n>,CTensor>::value, int
>::type
>
operator CVector<NumType,n>() const
{
...
}
};
And here is another approach using function overloading:
template <typename NumType,size_t... Sizes>
struct CTensor {
template<size_t n,size_t m>
CMatrix<NumType,n,m> convert() const
{
...
}
template<size_t n>
CVector<NumType,n> convert() const
{
...
}
template <typename T>
operator T() const { return convert<Sizes...>(); }
};
This is actually longer description of my comment: Why not having CTensor only and aliasing it as CVector / CMatrix? No conversion needed, they will become the same.
...it is solving the real problem in a completely different way than the title asks for. Just for the record :)
1) Hiding base implementation in namespace detail
2) Specializing what really needs to be specialized
(this can be done by some helper struct as well - specializing the struct providing the method)
3) Aliasing CVector/CMatrix as CTensor (no need for the operator then)
#include <vector>
namespace detail {
template<class T, std::size_t... Sizes>
class base;
template<class T, std::size_t Size>
class base<T, Size> {
std::vector<T> data;
public:
T& operator[](std::size_t i) {
return data[i]; }
};
template<class T, std::size_t First, std::size_t... More>
class base<T, First, More...> {
std::vector<base<T, More...>> data;
public:
// this could be done better, just an example
base<T, More...>& operator[](std::size_t i) {
return data[i]; }
};
}
template<class T, std::size_t... Sizes>
class CTensor: public detail::base<T, Sizes...> {};
//we can specialize CTensor<T, Size>
//and CTensor<T, Width, Height> here
template<class T, std::size_t Size>
using CVector = CTensor<T, Size>;
template<class T, std::size_t Width, std::size_t Height>
using CMatrix = CTensor<T, Width, Height>;
Make sizeof...(Sizes) a dependant argument, and make the type CMatrix/CVector correct (taking the correct number of template parameter).
Using:
template <std::size_t ... Is> struct index_sequence {};
template <std::size_t I, typename T> struct index_element;
template <std::size_t I, std::size_t ... Is>
struct index_element<I, index_sequence<Is...> >
{
private:
static constexpr const std::size_t a[] = {Is...};
public:
static_assert(I < sizeof...(Is), "out of bound");
static constexpr const std::size_t value = a[I];
};
then you may do:
template <
std::size_t N = sizeof...(Sizes),
typename std::enable_if<N == 1, int>::type = 0>
operator CVector<
T,
index_element<0, index_sequence<Sizes..., 0>
>::value>() const
{
// Your implementation
}
template <
std::size_t N = sizeof...(Sizes),
typename std::enable_if<N == 2, int>::type = 0>
operator CMatrix<
T,
index_element<0, index_sequence<Sizes..., 0>>::value
index_element<1, index_sequence<Sizes..., 0, 0>>::value
>() const
{
// Your implementation
}

Avoiding struct in variadic template function

Trying to search for this answer, I noticed that the titles of the questions
on variadic template functions are very uninformative when searching on
SO. You only known that the question is about variadic template. Hopefully my
question wasn't asked before and the title will help people finding it.
So, I want to make a function which has a variadic template parameter. More
precisely, as an example, let say I want to have a compile time permutation of
an array. Here is a working code:
template <typename... Ts> struct Sequence {};
template <typename T, unsigned Size, typename... SeqTis> struct Permute;
template <typename T, unsigned Size, typename... SeqTis>
struct Permute<T, Size, Sequence<SeqTis...> > {
using type = typename std::array<T, Size>;
constexpr static type permute(const type ar) {
return { (ar[SeqTis::value])... };
}
};
Then the following is perfectly legal:
using T0 = std::integral_constant<int, 0>;
using T1 = std::integral_constant<int, 1>;
using T2 = std::integral_constant<int, 2>;
using Perm120 = Permute<int, 3, Sequence<T1, T2, T0> >;
using arr3 = Perm120::type;
constexpr arr3 ar {5,7,2};
constexpr arr3 arPerm = Perm120::permute(ar);
I'm now trying to avoid using a structure so I wrote the following:
template <typename T, unsigned Size, typename... SeqTis>
constexpr typename std::array<T, Size>
permutefun<T, Size, Sequence<SeqTis...> >(const typename std::array<T, Size> ar) {
return { (ar[SeqTis::value])... };
}
And GCC refuse it saying that
essai.cpp:19:11: error: expected initializer before ‘<’ token
permutefun<T, Size, Sequence<SeqTis...> >(const typename std::array<T, Size> ar) {
^
Why is it so ?
To add to previous answers, you also need to have a convenient syntax for calling permutefun, but the current template parameters
template <typename T, unsigned Size, typename... SeqTis>
are not convenient because you'd have to call
permutefun <int, 3, T1, T2, T0>(ar);
A solution is to deduce arguments in two steps:
#include <array>
template <typename... Ts> struct Sequence {};
template <typename... SeqTis, typename T, unsigned long Size>
constexpr std::array<T, Size>
permutefun(Sequence<SeqTis...>, const std::array<T, Size> ar) {
return { (ar[SeqTis::value])... };
}
template <typename Seq, typename T, unsigned long Size>
constexpr std::array<T, Size>
permutefun(const std::array<T, Size> ar) {
return permutefun(Seq(), ar);
}
int main ()
{
using T0 = std::integral_constant<int, 0>;
using T1 = std::integral_constant<int, 1>;
using T2 = std::integral_constant<int, 2>;
using Perm120 = Sequence<T1, T2, T0>;
using arr3 = std::array <int, 3>;
constexpr arr3 ar = {5,7,2};
constexpr arr3 arPerm = permutefun <Perm120>(ar);
}
Now arguments T, Size appear last so are automatically deduced by the input array. Argument Seq comes first, but to deduce its "unpacked" parameters SeqTis... you need a call to a second overload of permutefun. This allows the convenient syntax
permutefun <Perm120>(ar);
The 2nd overload may be useful by itself, because it allows the alternative syntax
Perm120 perm;
permutefun(perm, ar);
Also note that std::array::operator[] is constexpr only in C++14. For instance, this does not compile with Clang 3.3 and -std=c++11.
Your syntax looks like an attempt at a partial function specialisation, but that's not what you want, so that's not what you should write. Since partial function specialisations aren't supported, the compiler gets confused by the unexpected <.
template <typename T, unsigned Size, typename... SeqTis>
constexpr std::array<T, Size>
permutefun(const std::array<T, Size> ar) {
// ^ removed the <...>
return { (ar[SeqTis::value])... };
}
Also, you don't need typename here, as std::array<T, Size> is already known to be a type. It's fine to leave it in, but it works just as well without it.
Do you mean:
template <typename T, unsigned Size, typename... SeqTis>
constexpr std::array<T, Size>
permutefun(const typename std::array<T, Size> ar) {
return { (ar[SeqTis::value])... };
}
partial template specialization is not possible for function.