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

  1. 为什么需要指定模板参数?
  2. 如何解决这个限制? (允许使用 C++11 或 C++14)。

(顺便说一句:我目前使用的是 Visual Studio 2010,如果我不使用 &lt;T&gt;,则会收到错误 C2896。)

【问题讨论】:

    标签: c++ templates c++11


    【解决方案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() 中的有效表达式。它编译得很好。
      • 模板参数推导在特定模式下不起作用,也许这回答了你的问题:stackoverflow.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】:

        AngewUrzeit 已经给出了为什么它不起作用的解释。

        我试图提供的是一种可能的解决方法来解决您的问题。但是,我无法准确判断这是否适合您的情况,因为 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 和以前一样。

        【讨论】:

          猜你喜欢
          • 2022-01-10
          • 1970-01-01
          • 2021-11-16
          • 2018-10-23
          • 2021-09-25
          • 2018-09-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多