【问题标题】:Template function as a template argument模板函数作为模板参数
【发布时间】:2011-01-15 00:01:14
【问题描述】:

我只是对如何在 C++ 中以通用方式实现某些东西感到困惑。有点绕,我一步一步解释。


考虑这样的代码:

void a(int) {
    // do something
}
void b(int) {
    // something else
}


void function1() {
    a(123);
    a(456);
}
void function2() {
    b(123);
    b(456);
}

void test() {
    function1();
    function2();
}

很容易注意到function1function2 的作用相同,唯一不同的部分是内部函数。

因此,我想将function 设为通用以避免代码冗余。我可以使用函数指针或模板来做到这一点。现在让我选择后者。 我的想法是这样更好,因为编译器肯定能够内联函数——我说的对吗?如果调用是通过函数指针进行的,编译器还能内联调用吗?这是一个附带问题。

好的,回到原点……带模板的解决方案:

void a(int) {
    // do something
}
void b(int) {
    // something else
}

template<void (*param)(int) >
void function() {
    param(123);
    param(456);
}

void test() {
    function<a>();
    function<b>();
}

一切正常。但我遇到了一个问题:如果 ab 本身是泛型,我还能这样做吗?

template<typename T>
void a(T t) {
   // do something
}

template<typename T>
void b(T t) {
   // something else
}

template< ...param... > // ???
void function() {
    param<SomeType>(someobj);
    param<AnotherType>(someotherobj);
}

void test() {
    function<a>();
    function<b>();
}

我知道模板参数可以是以下之一:

  • 一种类型,
  • 模板类型,
  • 一个类型的值。

这些似乎都没有涵盖我的情况。因此,我的主要问题是:我该如何解决,即在最后一个示例中定义 function()

(是的,在这种情况下,函数指针似乎是一种解决方法 - 只要它们也可以内联 - 但我正在寻找此类问题的通用解决方案)。 p>

【问题讨论】:

    标签: c++ templates dry generic-programming


    【解决方案1】:

    为了使用模板解决这个问题,您必须使用模板模板参数。 不幸的是,您不能将模板模板函数作为类型传递,因为它必须首先被实例化。但是有一个虚拟结构的解决方法。这是一个例子:

    template <typename T>
    struct a {
    
        static void foo (T = T ())
        {
        }
    
    };
    
    template <typename T>
    struct b {
    
        static void foo (T = T ())
        {
        }
    
    };
    
    struct SomeObj {};
    struct SomeOtherObj {};
    
    template <template <typename P> class T>
    void function ()
    {
        T<SomeObj>::foo ();
        T<SomeOtherObj>::foo ();
    }
    
    int main ()
    {
        function<a>();
        function<b>();
    }
    

    【讨论】:

    • 不完全确定为什么这被否决了。这并不完全令人满意,但它解决了问题。
    • 所以总结一下:可以内联调用的唯一解决方案是用仿函数替换函数?有点笨拙,但我认为完全可以接受。谢谢!
    • 但我承认,当你说不可能内联按地址调用时,我感到非常惊讶......如果可以在编译时确定地址等于给定函数的地址,我希望编译器足够聪明。 :) 奇怪...
    • 我用 g++ 4.5 运行过,结果相反:pastebin.com/eXbAyLPv
    • @Kos:你的测试太简单了,gcc 足够聪明,可以优化简单的静态代码块。尝试一些更接近现实的东西。顺便说一句,同时指定-O3-Os 是没有意义的。如果您使用多个 -O 选项,无论有无级别编号,最后一个这样的选项是有效的。
    【解决方案2】:

    使用 C++14 中的通用 lambda,您可以这样做:

    template<typename T> void a(T t) { /* do something */}
    template<typename T> void b(T t) { /* something else */ }
    
    template <typename F>
    void function(F&& f) {
        f(someobj);
        f(someotherobj);
    }
    
    void test() {
        // For simple cases, auto&& is even probably auto or const auto&
        function([](auto&& t){ a(t); });
        function([](auto&& t){ b(t); });
    
        // For perfect forwarding
        function([](auto&& t){ a(std::forward<decltype(t)>(t)); });
        function([](auto&& t){ b(std::forward<decltype(t)>(t)); });
    }
    

    如果调用是通过函数指针进行的,编译器还能内联调用吗?

    它们可以,但确实更复杂,而且它们可能比仿函数或模板更频繁地失败。

    【讨论】:

    • 通用 lambda 是 C++14 的一部分。
    • @mariusm:确实,措辞固定。
    【解决方案3】:

    这是一种方法。它可能不是最好的,但它确实有效:

    template <typename T, T param>
    void function() {
        param(123);
        param(456);
    }
    
    void test()
    {
        function< void(*)(int), a<int> >(); // space at end necessary to compiler
        function< void(*)(int), b<int> >(); // because the C++ grammar is ambiguous
    }
    

    它们是否被内联取决于编译器,但如果不是,我会相当惊讶。

    编辑:好的,我今天有点走神,错过了参数为不同类型的部分。我的错。

    使用模板可能有一个棘手的方法,但这是我能想到的最简单的方法:

    #define function(x) do { x<thing1>(obj1); x<thing2>(obj2) } while(0)
    

    我知道,我知道,“宏是邪恶的,”等等等等。有用。如果function 需要比您的示例更复杂,您可能会遇到问题,但它比我想出的任何东西都容易。

    【讨论】:

    • 但是请注意function会想要调用参数模板函数的不同实例化。
    • @Kos - #define 是不可能的吗?
    • 嗯,它 一个“最后的手段”,它会起作用:),但编辑、调试、不能在命名空间中很不舒服......我' d 更愿意找到基于模板而不是基于预处理器的解决方案。
    【解决方案4】:
    template < typename F >
    void function(F f)
    {
      f(123);
    }
    
    void a(int x) { ... }
    
    struct b { void operator() (int x) { ... } };
    
    void outer()
    {
      function(&a);
      function(b());
    }
    

    【讨论】:

    • 这不是 OP 想要的。
    • 谢谢,但这与我描述的问题无关。在这种情况下,给定的function 调用仅使用参数函数/函子的 1 个实例化。请再次阅读问题。