Reputation: 23610
I have the following code:
template <typename T>
void f1( T t )
{
std::cout << "f1( " << t << " ) called." << endl;
}
template <typename T>
void f2( T t )
{
std::cout << "f2( " << t << " ) called." << endl;
}
template <typename F, typename T>
void call( F && f, T t )
{
f( t );
}
template <typename T>
void foo( T t )
{
call( f1<T>, t ); // Why is <T> necessary?
// f1(t) is a valid expression!
call( f2<T>, t );
}
void bar()
{
foo( 1 );
}
In the function foo()
I need to specify the template argument, even though f1(t)
is a valid expression. That's kinda destroying some possibilities in my code. My questions:
(BTW: I'm currently using Visual Studio 2010 and I get the error C2896, if I leave the <T>
out.)
Upvotes: 6
Views: 971
Reputation: 23610
1. Why do I need to specify the template argument?
Well, f1
is not an object, but a function template. You can only pass objects to functions.
2. How can I work around that limitation? (C++11 or C++14 allowed).
Use an object with a templated operator()
. Just replace the definition of f1()
with
struct { template <typename T> void operator()( T t )
{
std::cout << "f1( " << t << " ) called." << endl;
} } f1;
and likewise for f2()
. In C++14 you can write it even nicer
static const auto f1 = []( auto t )
{
std::cout << "f1( " << t << " ) called." << endl;
};
Upvotes: 7
Reputation: 14421
There is a way to mimic passing function templates (or overload sets) around as first-class values: "reify" them by turning them into function objects. Your code could be rewritten like this:
struct F1
{
template <typename T>
void operator ()( T t )
{
std::cout << "f1( " << t << " ) called." << endl;
}
} f1;
struct F2
{
template <typename T>
void operator ()( T t )
{
std::cout << "f2( " << t << " ) called." << endl;
}
} f2;
// Note that this function didn't change at all!
template <typename F, typename T>
void call( F && f, T t )
{
f( t );
}
// Neither did this, expect that now you don't need the <T>
template <typename T>
void foo( T t )
{
call( f1, t );
call( f2, t );
}
void bar()
{
foo( 1 );
foo( 3.14 );
foo( "Hello World" );
}
Upvotes: 1
Reputation: 20503
The explanation why it doesn't work has been given by Angew and Urzeit.
What I'm trying to offer is a possible workaround for your problem. However, I cannot tell exactly whether this is appropriate or not for your case since the OP offers limited information on your specific designing requirements.
The first step is transforming f1
and f2
into template functor classes:
template <typename T>
class f1 {
public:
void operator ()( T t ) {
std::cout << "f1( " << t << " ) called." << std::endl;
}
};
(Similarly for f2
.) In this way, you can pass f1
(and not f1<T>
) as a template template parameter to call
which is now defined in this way:
template <template <typename> class F, typename T>
void call( T t )
{
F<T> f;
f( t );
}
Notice that F
is a template template parameter that binds to a template class taking one template type parameter (e.g. f1
).
Finally, foo
becomes this:
template <typename T>
void foo( T t )
{
call<f1>( t );
call<f2>( t );
}
bar
is left as before.
Upvotes: 0
Reputation: 3460
You could try to wrap the templated functions f1 and f2 in non-templated classes and pass instances (or even types) around, e.g.
struct F1
{
template <typename T>
void operator()(T t) const
{
std::cout << "F1::operator()(" << t << ") called" << std::endl;
}
};
struct F2
{
template <typename T>
void operator()(T t) const
{
std::cout << "F2::operator()(" << t << ") called" << std::endl;
}
};
template <typename F, typename T>
void call(F && f, T t)
{
f(t);
}
template <typename T>
void foo(T t)
{
static const F1 f1;
static const F2 f2;
call(f1, t);
call(f2, t);
}
void bar()
{
foo(1);
}
which produces the output:
F1::operator()(1) called
F2::operator()(1) called
Upvotes: 3
Reputation: 2909
f1(t)
is not a valid expression, since there is no function f1
. There is only a template named f1
from which a function f1<T>
can be generated on compile time.
This limitation you talk about is a direct consequence of the compile-time type-checking. If you know the type of the argument of foo at compile time, there is no limitation, since you cann add it easily. If you don't know the type of the argument, you might have to use a derived class model instead of a template-driven idea.
Upvotes: 1
Reputation: 171127
f1
is not a function, it's a template. You cannot pass a template as a function argument.
f1<T>
is a function, so it can be passed.
Upvotes: 11