为啥我需要在这里指定模板化函数的模板参数类型?
Posted
技术标签:
【中文标题】为啥我需要在这里指定模板化函数的模板参数类型?【英文标题】:Why do I need to specify the template argument type of a templated function here?为什么我需要在这里指定模板化函数的模板参数类型? 【发布时间】:2013-06-18 12:58:27 【问题描述】:我有以下代码:
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 );
在函数foo()
中我需要指定模板参数,即使f1(t)
是一个有效的表达式。这有点破坏我的代码中的一些可能性。我的问题:
-
为什么需要指定模板参数?
如何解决这个限制? (允许使用 C++11 或 C++14)。
(顺便说一句:我目前使用的是 Visual Studio 2010,如果我不使用 <T>
,则会收到错误 C2896。)
【问题讨论】:
【参考方案1】:f1
不是函数,而是模板。您不能将模板作为函数参数传递。
f1<T>
是一个函数,所以可以传递。
【讨论】:
【参考方案2】:1.为什么我需要指定模板参数?
嗯,f1
不是一个对象,而是一个函数模板。您只能将对象传递给函数。
2。我该如何解决这个限制? (允许使用 C++11 或 C++14)。
使用带有模板化operator()
的对象。只需将f1()
的定义替换为
struct template <typename T> void operator()( T t )
std::cout << "f1( " << t << " ) called." << endl;
f1;
同样适用于f2()
。在 C++14 中你可以写得更好
static const auto f1 = []( auto t )
std::cout << "f1( " << t << " ) called." << endl;
;
【讨论】:
感谢所有其他回答者的启发性意见。【参考方案3】:您可以尝试将模板函数 f1 和 f2 包装在非模板类中并传递实例(甚至类型),例如
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);
产生输出:
F1::operator()(1) 调用
F2::operator()(1) 调用
【讨论】:
+1 这是我在回答中使用的相同想法的一个很好的变体。但是,创建call
而不是接收F
,创建一个本地默认构造的实例化它可能是有用的(这取决于OP 的要求)。然后,在foo
中,我们可以使用call<F1>(t);
。【参考方案4】:
f1(t)
不是一个有效的表达式,因为没有函数f1
。只有一个名为f1
的模板,可以在编译时从中生成函数f1<T>
。
你所说的这个限制是编译时类型检查的直接后果。如果您在编译时知道 foo 的参数类型,则没有限制,因为您不能轻松添加它。如果你不知道参数的类型,你可能不得不使用派生类模型而不是模板驱动的想法。
【讨论】:
当然f1(t)
是foo()
中的有效表达式。它编译得很好。
模板参数推导在特定模式下不起作用,也许这回答了你的问题:***.com/questions/1268504/…
@RalphTandetzky:在foo
中使用的可能 是一个有效的表达式并不重要。模板不是宏;他们不只是拿走任何东西,然后将令牌粘贴在一起。您的模板函数采用F &&f
。那是一个函数参数;它必须是一个value,它有一个type。模板既不是值也不是类型。因此它们不能作为函数参数传递。
@RalphTandetzky:是的,f1(t)
是一个有效的表达式,因为函数模板参数推导。编译器可以根据参数t
的类型推断要调用的函数模板f1
的实例化——它将f1(t)
替换为f1<T>(t)
。这基本上只是一些语法糖。当你想传递f1
模板的实例时,编译器无法帮助你,你必须指定你想要的。【参考方案5】:
有一种方法可以模仿传递函数模板(或重载集)作为一等值:通过将它们转换为函数对象来“具体化”它们。你的代码可以这样重写:
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" );
【讨论】:
【参考方案6】:Angew 和Urzeit 已经给出了为什么它不起作用的解释。
我试图提供的是一种可能的解决方法来解决您的问题。但是,我无法准确判断这是否适合您的情况,因为 OP 提供的有关您的特定设计要求的信息有限。
第一步是将f1
和f2
转换为模板函子类:
template <typename T>
class f1
public:
void operator ()( T t )
std::cout << "f1( " << t << " ) called." << std::endl;
;
(类似f2
。)通过这种方式,您可以将f1
(而不是f1<T>
)作为模板模板参数传递给现在以这种方式定义的call
:
template <template <typename> class F, typename T>
void call( T t )
F<T> f;
f( t );
注意F
是一个模板模板参数,它绑定到采用一个模板类型参数的模板类(例如f1
)。
最后,foo
变成了这样:
template <typename T>
void foo( T t )
call<f1>( t );
call<f2>( t );
bar
和以前一样。
【讨论】:
以上是关于为啥我需要在这里指定模板化函数的模板参数类型?的主要内容,如果未能解决你的问题,请参考以下文章