【问题标题】:Is it possible to have a function(-name) as a template parameter in C++?是否可以在 C++ 中将函数(-name)作为模板参数?
【发布时间】:2011-04-30 03:05:02
【问题描述】:

我不想要函数指针开销,我只想要具有相同签名的两个不同函数的相同代码:

void f(int x);
void g(int x);

...

template<typename F>
void do_work()
{
  int v = calculate();
  F(v);
}

...

do_work<f>();
do_work<g>();

这可能吗?


为了消除可能的混淆:“模板参数”是指模板的参数/参数不是一个函数参数,其类型是模板化的。

【问题讨论】:

    标签: c++ templates function


    【解决方案1】:

    你的想法没问题,但你不是在传递一个类型,而是一个值(特别是一个函数指针>。或者,传递一个提供函数的模板策略 - 阅读 Andrei Alexandrescu 的 Modern C++ Design 是个好主意。

    #include <iostream>
    
    int f(int x) { return 2 * x; }
    int g(int x) { return -3 * x; }
    
    typedef int (*F)(int);
    
    template<F f> 
    int do_work() 
    { 
        return f(7);
    } 
    
    int main()
    {
        std::cout << do_work<f>() << '\n'
                  << do_work<g>() << '\n'; 
    }
    

    int calculate() { return 4; }
    
    struct F { int do_something_with(int x) { return 2 * x; } };
    struct G { int do_something_with(int x) { return -3 * x; } };
    // or, make these functions static and use Operator::do_something_with() below...
    
    template<typename Operation> 
    int do_work() 
    { 
        int v = calculate(7);
        return Operation().do_something_with(v);
    } 
    
    int main()
    {
        std::cout << do_work<F>() << '\n'
                  << do_work<G>() << '\n'; 
    }
    

    【讨论】:

    • +1 用于展示带有 fn 指针的体面解决方案。但是我认为这仍然会产生一些运行时开销来取消引用 fn 指针与 do_work_f() 和 do_work_g() 的复制和粘贴版本...?
    • @Martin:这是一个至关重要的问题——优化器。我不会赌这会被内联,而我会赌政策模板成员......
    • @Tony - 你能不能链接一些资源,(大约)描述一个模板策略的东西,你会采用它来解决这个问题?
    • return Operation(7); 应该是return Operation()(7);
    • @Martin:我稍微更改了上面的模板策略版本,以更直接地反映问题。虽然它有点冗长,但我倾向于为策略提供的东西使用函数名称 - 每个策略中都有多个函数是很常见的,而且它通常会使代码更易于理解。 ,@Stephane Rolland:对于更伟大的具体应用程序,请考虑std::sort 接受小于策略...通过不硬编码运算符
    【解决方案2】:

    一种极有可能生成直接函数调用的方法是使用静态成员函数,因为它没有给编译器选项:

    struct F { static void func(int x) { /*whatever*/ } };
    struct G { static void func(int x) { /*whatever*/ } };
    
    template<class T>
    void do_work() {
        T::func(calculate());
    }
    

    没有函数指针,没有临时变量,也没有不必要的this。当然,我什么都不保证,但是即使禁用了优化,生成的代码也应该是合理的。

    【讨论】:

    • +1 :非常好的解决方案。不适用于支持我的问题的问题,但作为一般解决方案非常好!
    【解决方案3】:

    您可以将函数指针作为模板参数,但函数对象更“C++”。但是,您可以以同时接受这两种变体的方式编写函数模板:

    #include <iostream>
    
    void f(int x)
    {
        std::cout << "inside function f\n";
    }
    
    struct g
    {
        void operator()(int x)
        {
            std::cout << "inside function object g\n";
        }
    };
    
    template <typename Functor>
    void do_work(Functor fun)
    {
        fun(42);
    }
    
    int main()
    {
        // template argument is automatically deduced
        do_work(&f);
        // but we could also specify it explicitly
        do_work<void(*)(int)>(&f);
    
        // template argument is automatically deduced
        do_work(g());
        // but we could also specify it explicitly
        do_work<g>(g());
    }
    

    在这里,名称Functor 暗示了可以通过f(x) 语法调用的任何类型。函数自然支持这种语法,对于函数对象,f(x)f.operator()(x) 的语法糖。

    【讨论】:

    • +1 ...它们编译为直接调用,在运行时没有函数指针调用开销。
    • @Doug:不能保证。不过,很有可能,因为例如std::sort 从优化中受益良多。
    • 您在那里的实现会将typename Fun 参数与函数原型void(*)(int) 匹配,然后对两个调用使用相同的实例化——而不是内联:这就是为什么你最终将函数指针传递为do_work(Fun fun) 的运行时参数。
    • +1 - 不值得 -1:但它可以接受一个函数对象就好了;这是灵活、标准的使用方法。
    • @MSalters - 绝对不能保证。我稍微误读了 FredOverflow 的答案 - 我认为 FredOverflow 有 do_work 接受模板非类型函数指针参数,如问题所示,而不是模板类型参数。非类型参数更有可能编译成直接调用。
    【解决方案4】:

    不,您需要使用operator() 将函数包装在包装类中。这是一个例子:

    class Functor_f
    {
    public:
        void operator()(int x)
        {
        }
    };
    
    class Functor_g
    {
        public:
        void operator()(int x)
        {
        }
    };
    
    
    
    template<typename F>
    void do_work()
    {
      F f;
     int v = calculate();
      f(v);
    }
    
    
    int main()
    {
        do_work<Functor_f>();
        do_work<Functor_g>();
    
    }
    

    您可以使用std::ptr_fun 自动为您执行此包装。例如:

    void f(int x)
    {
    }
    
    void g(int x)
    {
    }
    
    template<typename F>
    void do_work(F f)
    {
     int v = calculate();
      f(v);
    }
    
    
    int main()
    {
        do_work(std::ptr_fun(f));
        do_work(std::ptr_fun(g));
    
    }
    

    【讨论】:

    • 第一个例子(没有std::ptr_fun)提供了更好的实际内联机会——并且(当然它取决于很多)因此很可能是最快的,也许有点违反直觉。干杯,
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-06
    相关资源
    最近更新 更多