I'd like to define a function for template class with an integer template parameter so that the number of function arguments depends on the template parameter. Here's an example:
template< class Coord, int dim >
class Point {
Coord mCoords[ dim ];
public:
void Set( /* I want exactly dim Coord arguments here. */ );
};
I'd like this code to compile:
Point<double,2> pt2d;
pt2d.Set( 25, 32 );
Point<double,3> pt3d;
pt3d.Set( 25, 32, 100 );
and this code to fail:
Point<double,2> pt2d;
pt2d.Set( 25, 32, 100 ); // Too many arguments
Point<double,3> pt3d;
pt3d.Set( 25, 32 ); // Too few arguments
Now, I can manually specialize Point
in smaller dimensions to have unrelated Set
functions, but I find the practice of essentially repeating the same code un-C++-ish. Furthermore, I shouldn't have to specialize for every possible value of the int template parameter.
Is it possible to implement Point<Coord,dim>::Set()
function that would take exactly dim
arguments of type Coord
without writing specialization code for each value of dim
?
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.
8. Why we use :: template-template parameter? Explanation: It is used to adapt a policy into binary ones.
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
You could use the trick that Boost.Hana uses for getNth
:
template <typename Coord, int dim, typename = std::make_index_sequence<dim>>
struct Point;
template <typename Coord, int dim, size_t... Ignore>
struct Point<Coord, dim, std::index_sequence<Ignore...>>
{
void Set(decltype(Ignore, Coord{})... args)
{
// ...
}
};
A longer version that hides the Ignore
ugliness a little bit (and works for non-default-constructible Coord
s... ) would be to add some metaprogramming boilerplate:
template <typename... > struct typelist { };
template <int N, typename T, typename = std::make_index_sequence<N>>
struct repeat;
template <int N, typename T>
using repeat_t = typename repeat<N, T>::type;
template <int N, typename T, size_t... Idx>
struct repeat<N, T, std::index_sequence<Idx...>>
{
template <size_t >
struct makeT { using type = T; };
using type = typelist<typename makeT<Idx>::type...>;
};
And then specialize on repeat_t
instead. And hide this in a namespace so a user can't mess it up:
namespace details {
template <typename Coord, int dim, typename = repeat_t<dim, Coord>>
struct Point;
template <typename Coord, int dim, typename... dimCoords>
struct Point<Coord, dim, typelist<dimCoords...>>
{
void Set(dimCoords... args)
{
}
};
}
template <typename Coord, int dim>
using Point = details::Point<Coord, dim>;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With