Passing a Variable as a Template Argument

Passing a variable as a template argument

What is the value of i (that is not a constant) at compile time? There is no way to answer unless executing the loop. But executing is not "compiling"
Since there is no answer, the compiler cannot do that.

Templates are not algorithm to be executed, but macros to be expanded to produce code.
What you can do is rely on specialization to implement iteration by recursion, like here:

#include <iostream>

template<int i>
void modify()
{ std::cout << "modify<"<<i<<">"<< std::endl; }

template<int x, int to>
struct static_for
{
void operator()()
{ modify<x>(); static_for<x+1,to>()(); }
};

template<int to>
struct static_for<to,to>
{
void operator()()
{}
};

int main()
{
static_for<0,10>()();
}

Note that, by doing this, you are, in fact, instantiating 10 functions named
modify<0> ... modify<9>, called respectively by static_for<0,10>::operator() ... static_for<9,10>::operator().

The iteration ends because static_for<10,10> will be instantiated from the specialization that takes two identical values, that does nothing.

How to pass a template function as an argument in C++?

You need to specify the template argument for display explicitly:

iter(arr, 3, display<int>);

Or make iter taking function pointer:

template <class T_arr>
void iter(T_arr *arr, int len, void (*func)(T_arr))
{
for(int i = 0; i < len; i++)
{
func(arr[i]);
}
}

then you can

iter(arr, 3, display); // template argument gets deduced as int

Pass an array (variable) as template argument

Declaring a template to have a template parameter of type int[5] actually causes it to have a template parameter of type int*. This is similar to the decay that occurs in function signatures. To fix this, simply declare fun to take its array template argument by reference instead:

template<const INT5& i5> void fun();

You want to pass the array {2, 3, 1, 2, 4} as the template argument to fun. That's doable, but it has to obey the restrictions on glvalue constant expressions in [expr.const]/5 (C++17); it must refer to an object with static storage duration. Therefore, you should make i5 static:

static constexpr int i5[5] = { 2,3,1,2,4 };
fun<i5>(); // this should work

Can a variable template be passed as a template template argument?

Short answer: No.

Long answer: Yes you can using some indirection through a class template:

template<typename T>
constexpr auto zero = T{0};

template<typename T>
struct zero_global {
static constexpr auto value = zero<T>;
};

template<typename T, template<typename> class VariableTemplate>
constexpr auto add_one()
{
return VariableTemplate<T>::value + T{1};
}

int main()
{
return add_one<int, zero_global>();
}

Live example

C++ convert variables to template arguments

I decided to have some more fun with the code, here's an improved version over my first attempt which has the following benefits:

  • Supports enum types
  • Explicitly specify how many parameters should be converted
  • Generic implementation for the complicated part, one small helper for each function that uses it.

The code:

#include <iostream>
#include <utility>
#include <type_traits>

// an enum we would like to support
enum class tribool { FALSE, TRUE, FILE_NOT_FOUND };

// declare basic generic template
// (independent of a specific function you'd like to call)
template< template< class > class CB, std::size_t N, typename = std::tuple<> >
struct var_to_template;

// register types that should be supported
template< template< class > class CB, std::size_t N, typename... Cs >
struct var_to_template< CB, N, std::tuple< Cs... > >
{
// bool is pretty simple, there are only two values
template< typename R, typename... Args >
static R impl( bool b, Args&&... args )
{
return b
? var_to_template< CB, N-1, std::tuple< Cs..., std::true_type > >::template impl< R >( std::forward< Args >( args )... )
: var_to_template< CB, N-1, std::tuple< Cs..., std::false_type > >::template impl< R >( std::forward< Args >( args )... );
}

// for each enum, you need to register all its values
template< typename R, typename... Args >
static R impl( tribool tb, Args&&... args )
{
switch( tb ) {
case tribool::FALSE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FALSE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::TRUE:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::TRUE > > >::template impl< R >( std::forward< Args >( args )... );
case tribool::FILE_NOT_FOUND:
return var_to_template< CB, N-1, std::tuple< Cs..., std::integral_constant< tribool, tribool::FILE_NOT_FOUND > > >::template impl< R >( std::forward< Args >( args )... );
}
throw "unreachable";
}

// in theory you could also add int, long, ... but
// you'd have to switch on every possible value that you want to support!
};

// terminate the recursion
template< template< class > class CB, typename... Cs >
struct var_to_template< CB, 0, std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return CB< std::tuple< Cs... > >::template impl< R >( std::forward< Args >( args )... );
}
};

// here's your function with the template parameters
template< bool B, tribool TB >
int HeavyLoop_impl( int arg )
{
for( int i = 0; i < 10000000; i++ ) {
arg += B ? 1 : 2;
arg += ( TB == tribool::TRUE ) ? 10 : ( TB == tribool::FALSE ) ? 20 : 30;
}
return arg;
}

// a helper class, required once per function that you'd like to forward
template< typename > struct HeavyLoop_callback;
template< typename... Cs >
struct HeavyLoop_callback< std::tuple< Cs... > >
{
template< typename R, typename... Args >
static R impl( Args&&... args )
{
return HeavyLoop_impl< Cs::value... >( std::forward< Args >( args )... );
}
};

// and here, everything comes together:
int HeavyLoop( bool b, tribool tb, int arg )
{
// you provide the helper and the number of arguments
// that should be converted to var_to_template<>
// and you provide the return type to impl<>
return var_to_template< HeavyLoop_callback, 2 >::impl< int >( b, tb, arg );
}

int main()
{
bool b = true;
tribool tb = tribool::FALSE;
int arg = 0;
int res = HeavyLoop( b, tb, arg );
std::cout << "res: " << res << std::endl;
return 0;
}

And here's a live example in case you want to play with it.

How to pass a reference to a template typename argument

You're looking for Foo<decltype(a) &> foo1(a).

A more obscure alternative (which works in this specific case) is Foo<decltype((a))> foo1(a).



Related Topics



Leave a reply



Submit