Why do I need to specify the type argument of the template function template here?

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 foo() function, I need to specify a template argument, even if f1(t) is a valid expression. This is kind of breaking some features in my code. My questions:

  • Why do I need to specify a template argument?
  • How can I get around this limitation? (Allowed C ++ 11 or C ++ 14).

(BTW: I am currently using Visual Studio 2010, and I am getting error C2896 if I leave <T> out.)

+6
source share
6 answers

f1 not a function, it is a pattern. You cannot pass a template as an argument to a function.

f1<T> is a function, so it can be passed.

+11
source

1. Why do I need to specify a template argument?

Well, f1 not an object, but a function template. You can only pass objects in functions.

2. How can I get around this limitation? (Allowed C ++ 11 or C ++ 14).

Use an object with an operator() pattern. Just replace the definition of f1() with

 struct { template <typename T> void operator()( T t ) { std::cout << "f1( " << t << " ) called." << endl; } } f1; 

as well as for f2() . In C ++ 14 you can write even nicer

 static const auto f1 = []( auto t ) { std::cout << "f1( " << t << " ) called." << endl; }; 
+8
source

You can try to wrap the template functions f1 and f2 in non-templated classes and pass instances (or even types) around, for example.

 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 conclusion:

F1 :: operator () (1) is called

F2 :: operator () (1) is called

+3
source

f1(t) not a valid expression since there is no function f1 . There is only a template called f1 from which the function f1<T> can be generated at compile time.

This limitation you are talking about is a direct result of compilation type checking. If you know the type of the argument foo at compile time, there are no restrictions, since you cannot easily add it. If you don't know the type of argument, you may need to use a model of a derived class instead of a template-based idea.

+1
source

There is a way to simulate passing function templates (or overload kits) as first-class values: "reify" them by turning them into functional objects. Your code can be rewritten as follows:

 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" ); } 
+1
source

An explanation of why this does not work was given by Angew and Urzeit .,

What I'm trying to offer is a possible solution to the problem. However, I cannot say for sure whether this is suitable for your case, as the OP offers limited information about your specific design requirements.

The first step is to convert f1 and f2 into template template classes:

 template <typename T> class f1 { public: void operator ()( T t ) { std::cout << "f1( " << t << " ) called." << std::endl; } }; 

(Similarly for f2 .) This way you can pass f1 (not f1<T> ) as a template template parameter to call , which is now defined this way:

 template <template <typename> class F, typename T> void call( T t ) { F<T> f; f( t ); } 

Note that F is a template template parameter that binds to a template class with one template type parameter (for example, f1 ).

Finally, foo will be as follows:

 template <typename T> void foo( T t ) { call<f1>( t ); call<f2>( t ); } 

bar remains as before.

0
source

Source: https://habr.com/ru/post/947551/


All Articles