为啥我需要在这里指定模板化函数的模板参数类型?

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,如果我不使用 &lt;T&gt;,则会收到错误 C2896。)

【问题讨论】:

【参考方案1】:

f1 不是函数,而是模板。您不能将模板作为函数参数传递。

f1&lt;T&gt;是一个函数,所以可以传递。

【讨论】:

【参考方案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&lt;F1&gt;(t);【参考方案4】:

f1(t) 不是一个有效的表达式,因为没有函数f1。只有一个名为f1模板,可以在编译时从中生成函数f1&lt;T&gt;

你所说的这个限制是编译时类型检查的直接后果。如果您在编译时知道 foo 的参数类型,则没有限制,因为您不能轻松添加它。如果你不知道参数的类型,你可能不得不使用派生类模型而不是模板驱动的想法。

【讨论】:

当然f1(t)foo() 中的有效表达式。它编译得很好。 模板参数推导在特定模式下不起作用,也许这回答了你的问题:***.com/questions/1268504/… @RalphTandetzky:在foo 中使用的可能 是一个有效的表达式并不重要。模板不是宏;他们不只是拿走任何东西,然后将令牌粘贴在一起。您的模板函数采用F &amp;&amp;f。那是一个函数参数;它必须是一个value,它有一个type。模板既不是值也不是类型。因此它们不能作为函数参数传递。 @RalphTandetzky:是的,f1(t) 是一个有效的表达式,因为函数模板参数推导。编译器可以根据参数t 的类型推断要调用的函数模板f1 的实例化——它将f1(t) 替换为f1&lt;T&gt;(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 提供的有关您的特定设计要求的信息有限。

第一步是将f1f2 转换为模板函子类:

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

(类似f2。)通过这种方式,您可以将f1(而不是f1&lt;T&gt;)作为模板模板参数传递给现在以这种方式定义的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 和以前一样。

【讨论】:

以上是关于为啥我需要在这里指定模板化函数的模板参数类型?的主要内容,如果未能解决你的问题,请参考以下文章

构造函数、模板和非类型参数

C++模板

C++模板

用模板类型指定模板类型的类型

为啥尝试使用显式类型参数调用模板成员函数时会出错?

C++模板详解:泛型编程模板原理非类型模板参数模板特化分离编译