I have a template class called Array
template<typename T, int dimension>
class Array<typename T, int dimension>{
//definition of the class
}
I want to write a non-member function cast such that I can cast Array into different type. For example
Array<int, 2> a;
Array<float, 2> b = cast<float>(a);
How should I write this function? I am more interested in how to declare it instead of how to implement the detailed casting. I have tried
template<template<typename T, int dimension> class Array, typename New_T, int dimension>
Array<typename New_T, int dimension> cast(Array<typename T, int dimension> a){
// detailed implementation of casting, which I do not care for this question.
}
but it cannot pass the compilation.
You don't need template template parameters at all here. Simple typename and int parameters will do:
template <typename T, int dimension>
class Array
{
// ...
};
template <typename NewT, typename T, int dimension>
Array<NewT, dimension> cast(const Array<T, dimension>& a)
{
// ...
}
Live Demo
You only need template template parameters when you want to accept different types of templates. For instance, if you wanted cast to be able to accept an Array or a std::array, you could use a template template parameter:
template<typename NewT, typename T, auto dimension, template<typename, auto> typename ArrayT>
ArrayT<NewT, dimension> cast(const ArrayT<T, dimension>& a)
{
// ...
}
Live Demo
Note in this case I also changed the type of dimension to auto since std::array uses a size_t for its dimension while your Array uses int.
How should I write this function? I am more interested in how to define it instead of how to implement the detailed casting.
I suppose something like
template <typename ToT, typename FromT, int Dim>
Array<ToT, Dim> cast (Array<FromT, Dim> const & inA)
{
// ...
}
It's useful place ToT (to-type) in first position so you can explicit it and let FromT and Dim deduced from the inA value.
--- EDIT ---
The OP asks
Any insight why I have to put it [ToT] in the first position?
You don't necessarily have to put ToT in first position. But this simplify your life.
The point is that FromT and Dim are deducible from the inA argument; ToT isn't deducible from arguments so you have to explicit it.
But if you want to explicit a template parameter, you necessarily have to explicit the preceding parameters. So if you put ToT in last position, you have to call cast() explicating all template parameters
cast<int, 2, float>(a);
If you place ToT in first position, you have to explicit only it and leave the compiler deduce FromT and Dim from the argument
cast<float>(a);
Related
I am trying to learn more about templates in C++. I would like to be able to call a function where I pass it a type and the length as an argument. Is this possible?
template <class T>
void alloc_arr (int l) {
std::allocator<T[l]> a;
}
alloc_arr<int[]>(64);
It doesn't work because the instantiated type must be fixed at compile time (T[l] is not fixed).
Is there some other way to do this which doesn't require the length to be specified in the type (<T[64]>)?
Is there some other way to do this which doesn't require the length to be specified in the type ()?
In some way, you need to pass it as template parameter
You can pass it explicitly, as suggested by Lourens Dijkstra
template <typename T, std::size_t Dim>
void alloc_arr ()
{
std::allocator<T[Dim]> a;
// ...
}
or, if you can use at least C++11, also you can deduce it from the type of an argument; by example,
template <typename T, std::size_t Dim>
void alloc_arr (std::integral_constant<std::size_t, Dim> const &)
{
std::allocator<T[Dim]> a;
// ...
}
or also
template <typename T, typename U>
void alloc_arr (U const &)
{
std::allocator<T[U::value]> a;
// ...
}
calling alloc_arr with a std::integral_constant<std::size_t, 5u>{}, by example.
You could pass the size as a template parameter:
template <class T, size_t size>
void alloc_arr() { ... }
This is the only way. A couple of days ago I found out that passing a constexpr lambda as a regular parameter is considered ill-formed: Trying to pass a constexpr lambda and use it to explicitly specify returning type
Also, note that type T should be int; not int[].
So, calling alloc_arr:
alloc_arr<int, 64>();
I'm trying to do an advanced class template argument deduction by using the new deduction guides from c++17. Unfortunately, it looks like you can only use simple template declarations after the ->, but I need a helper struct to determine the resulting type.
My use case is this one: I have a variadic template class that takes an arbitrary amount of different types. For one constructor I want to specify every single one, for another ctor I want to specify only one type and replicate it N times.
To access this N in the deduction guide I introduced a new type:
template<size_t N>
struct Replicate { };
The class I have is similar this one:
template<typename... Foos>
struct Foo {
// [ ... ] member std::tuple<Foos...>
// ctor 1: give values for all types (easy to deduce)
Foo(Foos&&... args /* rvalue ref, NOT forwarding */) { };
// ctor 2: give one value and N, result is N copies of this value.
// takes Replicate<N> as parameter to aid the deduction.
template<typename T, size_t N>
Foo(const T& t, Replicate<N>) { };
};
The usage would be like this:
Foo f1{1, 2, 3.0}; // deduce Foo<int, int, double>;
Foo f2{8, Replicate<4>{}}; // deduce Foo<int, int, int, int>;
The deduction guide for the first one is straight forward:
template<typename... Ts>
Foo(Ts&&...) -> Foo<Ts...>;
It gets problematic with the second (ctor 2) deduction guide. First I need a helper struct to create Foo<T, T, ... /* total N times */, T> from T and N.
template<typename, typename, typename>
struct ExpandNTimes;
template<typename T, size_t... Is>
struct ExpandNTimes<T, std::index_sequence<Is...>> {
template<size_t> using NthType = T;
using Type = Foo<NthType<Is>...>;
};
Then in the deduction guide I want to utilize the helper to deduce the correct type. I cant directly use Foo<something> as there is no kind of "in place parameter pack creation", therefore the helper struct.
template<typename T, size_t N>
Foo(const T&, Replicate<N>) -> typename ExpandNTimes<T, std::make_index_sequence<N>>::Type;
Unfortunately this results int an error similar to this one:
error: trailing return type of 'typename ExpandNTimes<T, /* std things */>::Type' deduction guide is not a specialization of ‘Foo<Ts>’
Is there any way to work around this issue?
This is impossible with class template argument deduction - both template names must be the same, and the thing after the -> must be a simple-template-id. This doesn't leave any room for template shenanigans.
But nothing prevents you from doing the thing that class template argument deduction is intended to replace: factory functions:
template <typename T, size_t N>
typename ExpandNTimes<T, std::make_index_sequence<N>>::Type
makeFoo(T const&, Repliace<N>);
This is doable if you can change Replicate's definition to embed a pack into a base class:
template<class> struct ReplicateBase {};
template<size_t N> struct Replicate : ReplicateBase<std::make_index_sequence<N>> {};
template<size_t, class T> using Meow = T;
template<typename T, size_t... Ns>
Foo(const T&, ReplicateBase<std::index_sequence<Ns...>>) -> Foo<Meow<Ns, T>...>;
Then it's a "simple" matter of constraining everything else to not compete with this guide when passed a Replicate:
Foo(Foos&&... args) { } and template<typename... Ts> Foo(Ts&&...) -> Foo<Ts...>; (are you sure you want to deduce references when passed lvalues?) should be constrained to when Foos/Ts aren't Replicates
template<typename T, size_t N> Foo(const T& t, Replicate<N>); needs to be constrained to prevent it from being used to deduce an empty pack (e.g., to when sizeof...(Foos) == N)
I'm writing a container storage class template that wraps a private std::array in order to add some functionality to it. The template parametrises the number of values, as follows:
template<size_t N> class Vector {
private:
array<double, N> vals;
public:
[...]
};
I'd like the constructor for the class to only accept N doubles to fill the array, but I can't find a good way to do this. Variadic arguments don't provide a mechanism to check how many of them there are, so they're right out. Parameter packs don't do floating-point promotion, but I'd be willing to deal with that if I could only figure out how to use them for this.
I've tried following the approach in the answer to Member function template with the number of parameters depending on an integral template parameter but I can't understand the significance enable_if<>::type=0 section. I've tried naïvely copying that code in (though I'd much rather understand how it works. I've seen people use ::value in other places but I can't find any documentation on why) but expanding the resulting parameter pack doesn't seem to work. My other concern with parameter packs is that I'm not sure that they'd ensure the types of all arguments were the same.
I've tried running a static_assert on the size of an initializer list, in the body of the constructor, but of course the size of the list is not constant at compile time, so that doesn't work either.
Is there a standard approach here? Am I just using parameter packs wrong?
Update:
I've got the approach in the answer I linked above partly working:
template<size_t N> class Vector {
private:
array<double, N> vals;
public:
template <typename ...T,
typename enable_if<sizeof...(T) == N, int>::type = 0>
Vector(T ...args) {
vals = {args...};
}
};
The issue is now that the enable_if term in the template means that when I initialise a Vector with, for example,
Vector<3> V {1.0, 2.0, 3.0};
It requests a template specialisation Vector<3>::Vector<double, double, double, 0> rather than <double, double, double>. How do I get rid of this stray term in the template?
Don't get what you mean by this:
Variadic arguments don't provide a mechanism to check how many of
them there are, so they're right out
template <typename ...T>
Vector(T... args) {
static_assert(sizeof...(args) <= N, "oops");
}
Should work..
You could additionally generate a pack of the right size via some template specialization tricks:
template <size_t N, class = std::make_index_sequence<N>>
class Vector;
template <size_t N, size_t... Is>
class Vector<N, std::index_sequence<Is...>>
{
private:
std::array<double, N> vals;
template <size_t >
using double_ = double;
public:
Vector(double_<Is>... vals)
{
...
}
};
That is a non-template constructor which takes N doubles.
By following Member function template with the number of parameters depending on an integral template parameter and Variadic templates with exactly n parameters, have got it working with the following code.
template<size_t N> class Vector {
private:
array<double, N> vals;
public:
template <typename ...T,
typename enable_if<sizeof...(T) == N, int>::type = 0>
Vector(T ...args) : vals{args} {}
};
Consider the following structs:
//Implementations provided elsewhere
struct A { A(int i, double d, std::string s); /* ... */ };
struct B { B(double d1, double d2); /* ... */ };
I have two conversion classes whose template signatures look like:
TupleAs< A, int, double, std::string > via1 { ... };
ArrayAs< B, double, 2 > via2 { ... };
Predictably, TupleAs converts a triplet of int,double, and std::string values into an object of type A. Similarly, ArrayAs converts a pair of two double values into an object of type B. (And yes, there are reasons why I cannot call the A and B constructors directly.)
Improving the syntax
I would like to change the syntax so I can do the following:
TupleAs< A(int,double,std::string) > via1 { ... };
ArrayAs< B(double,2) > via2 { ... };
which, I think, is more descriptive of a conversion process. The TupleAs template declaration and corresponding partial specialization would look like this:
template <typename T> struct TupleAs;
template <typename T, typename ... Args>
struct TupleAs<T(Args...)> { ... };
Compiler errors
However, if I try to do something similar with the ArrayAs version:
template <typename T> struct ArrayAs;
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
I get the following errors in clang (3.6) when trying to instantiate it (ArrayAs< B(double,2)> test;):
typeAs.cpp:14:22: error: unknown type name 'N'
struct ArrayAs<T(U,N)>{
^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
struct ArrayAs<T(U,N)>{
^~~~~~~~~~~~~~~
typeAs.cpp:13:45: note: non-deducible template parameter 'N'
template<typename T, typename U, unsigned N>
^
The gcc error diagnostic is a little different, but I won't post it here.
I admit that my templating skills should be better than they are, and I also concede that an analogous std::function<B(double,2)> declaration clearly is nonsense. But can someone tell me why the particular syntax I'm trying to achieve is not allowed? I looked through the C++14 standard and had trouble finding the relevant portion, and I'm having trouble interpreting the clang diagnostic message.
When you specialize TupleAs:
template <typename T, typename ... Args>
struct TupleAs<T(Args...)>
You are basically overloading the notation for a function. You are specializing on a function that takes Args... and returns a T. That is a type. You may not be using that function as a function, or really ever think about it as being a type, but that is what it is.
On the other hand, here:
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
There is no such thing as a function that takes N. It could take unsigned, but it can't take a value. There is just no such reasonable thing. From your example, B(double, 2) simply does not make sense. At best, you could write something that would allow:
template <unsigned N> using size_ = std::integral_constant<size_t, N>;
ArrayAs< B(double,size_<2>) >
Or even:
ArrayAs< B(std::array<double, 2>) >
since now we're back to using types everywhere. Whether you prefer that or not is personal preference.
The key here is that types are first-class citizens when it comes to all things template metaprogramming, and values should be avoided where possible.
template <typename T> struct ArrayAs;
template <typename T, typename U, std::size_t N>
struct ArrayAs<T(std::array<U,N>)> { ... };
works, as would:
template<class T>
struct to_array;
template<class T, size_t N>
struct to_array< T[N] > { using type = std::array<T, N>; };
template<class T>
using arr = typename to_array<T>::type;
then:
ArrayAs< Bob( arr<int[3]> ) > some_var;
live example.
Sadly, directly using ArrayAs< Bob( int[3] ) > doesn't work due to how arrays in function types decay to pointers.
How to define method signature so it will accept same number of arguments as variadic template class definition? For example how to define an Array class:
template<typename T, int... shape>
class Array
{
public:
T& operator () (???);
};
So you will be able to call it like this:
Array<int, 3, 4, 5> a;
a(1, 2, 3) = 2;
template<class T, int...Shape>
class Array {
template<int>using index_t=int; // can change this
public:
T& operator()(index_t<Shape>... is);
};
or:
template<class T, int...Shape>
class Array {
public:
T& operator()(decltype(Shape)... is);
};
or:
template<class T, int...Shape>
class Array {
public:
T& operator()(decltype(Shape, int())... is);
};
if you want to be able to change the type of the parameter to be different than Shape.
I find the decltype harder to understand a touch than the using, especially if you want to change the type of the parameter to be different than int.
Another approach:
template<class T, int...Shape>
class Array {
public:
template<class...Args,class=typename std::enable_if<sizeof...(Args)==sizeof...(Shape)>::type>
T& operator()(Args&&... is);
};
which uses SFINAE. It does not enforce that the Args are integer types however. We could add another clause if we wanted to (that all of the Args are convertible to int, say).
Yet another approach is to have your operator() take a package of values, like a std::array<sizeof...(Shape), int>. Callers would have to:
Array<double, 3,2,1> arr;
arr({0,0,0});
use a set of {}s.
A final approach would be:
template<class T, int...Shape>
class Array {
public:
template<class...Args>
auto operator()(Args&&... is) {
static_assert( sizeof...(Args)==sizeof...(Shapes), "wrong number of array indexes" );
}
};
where we accept anything, then generate errors if it is the wrong number of arguments. This generates very clean errors, but does not do proper SFINAE operator overloading.
I would recommend tag dispatching, but I don't see a way to make it much cleaner than the SFINAE solution, with the extra decltype and all, or better error messages than the static_assert version on the other hand.
I assume you want your arguments to be all of the same type, probably using an integer type (I'll just use int). An easy approach is to leverage the parameter pack you already have:
template <int>
struct shape_helper { typedef int type; };
template <typename T, int... Shape>
class Array
{
public:
T& operator()(typename shape_helper<Shape>::type...);
};