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

时间:2013-06-18 12:58:27

标签: c++ templates c++11

我有以下代码:

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)是有效表达式。这有点破坏了我的代码中的一些可能性。我的问题:

  1. 为什么我需要指定模板参数?
  2. 我如何解决这个限制? (允许使用C ++ 11或C ++ 14)。
  3. (顺便说一句:我目前正在使用Visual Studio 2010,如果我将<T>退出,我会收到错误C2896。)

6 个答案:

答案 0 :(得分:11)

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

f1<T>是一个函数,因此可以传递。

答案 1 :(得分:8)

<强> 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;
};

答案 2 :(得分: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)名为

答案 3 :(得分:1)

f1(t)不是有效表达式,因为没有函数f1。只有一个名为f1模板,可以在编译时生成函数f1<T>

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

答案 4 :(得分:1)

有一种方法可以模仿传递函数模板(或重载集)作为第一类值:通过将它们转换为函数对象来“reify”它们。您的代码可以像这样重写:

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

答案 5 :(得分:0)

AngewUrzeit提供了无法解释的原因。

我想提供的是针对您的问题的可能解决方法。但是,由于OP提供的有关您特定设计要求的信息有限,我无法确切地说明这是否适合您的情况。

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

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与以前一样。