模板函数作为模板参数

42

我刚刚在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()

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

4个回答

44
为了解决模板问题,您需要使用模板模板参数。不幸的是,您无法将模板模板函数作为类型传递,因为它必须首先实例化。但是,有一个使用虚拟结构体的解决方法。以下是一个示例:
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>();
}

1
不太确定为什么这个被踩了。虽然不完全令人满意,但它解决了问题。 - Chris Lutz
2
总结一下:唯一能够使调用内联化的解决方案就是将函数替换为函数对象吗?虽然有点繁琐,但我认为完全可以接受。谢谢! - Kos
1
但我承认,在你说不可能内联一个按地址调用之后,我感到非常惊讶...如果在编译时可以确定地址等于给定函数的地址,我会期望编译器足够聪明。 :) 奇怪... - Kos
我得说,Vlad,那很棒,但是算了,它能用,并且符合要求。 - WhozCraig
@ChefGladiator 实际上,请参考下面 Jarod42 的答案 https://dev59.com/Wm445IYBdhLWcg3wytIU#56399349 - mariusm
显示剩余6条评论

7

使用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
@mariusm:确实,措辞已经修正。 - Jarod42

0

这是一种方法。可能不是最好的,但它能起作用:

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
@Kos - “#define” 是不是不可行的选项? - Chris Lutz
1
嗯,这确实是一个“最后的手段”,可以起作用:),但它不方便编辑、调试,也不能在命名空间中使用……我更喜欢找到一个基于模板而非预处理器的解决方案。 - Kos

-2
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());
}

2
这不是原帖作者想要的。 - Chris Lutz
2
谢谢,但这与我所描述的问题无关。在这种情况下,给定的“函数”调用仅使用参数函数/函数对象的1个实例化。请再次阅读问题。 - Kos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接