指向可变参数模板静态函数的指针。怎么样?

时间:2014-11-21 16:01:12

标签: c++ c++11 function-pointers variadic-templates

我有一个代码:

class Factory
{
public:
    template<typename ...Args>
    static void testFunc(Args&& ...args)
    {
        cout << "inside function";
    }
};

是否可以创建指向 testFunc 的指针? 我所能做的就是像这样定义:

// main
void(*pFunc)() = &Factory::testFunc;
pFunc();

这有效,但我不能传递可变数量的参数,如下所示:

void(*pFunc)(WHAT_TO_TYPE_HERE?) = &Factory::testFunc;
pFunc(10, false, 'a', 11.5);

P.S。:省略号(...)每件事都有效。

5 个答案:

答案 0 :(得分:3)

  

是否可以创建指向testFunc

的指针

testFunc的声明定义了一系列函数,由Args参数化。每个函数都有自己的内存地址。因此,您只能指向testFunc定义的特定功能之一,例如testFunc<int, double>testFunc<>

而不是编写以下内容来获取指向testFunc

的0参数重载的指针
void(*pFunc)() = &Factory::testFunc;
你应该写

void(*pFunc)() = &Factory::testFunc<>;

因为没有Factory::testFunc的{​​{1}}是重载集,而不是函数。

答案 1 :(得分:1)

答案:

void(*pFunc)(WHAT_TO_TYPE_HERE?) = &Factory::testFunc;
pFunc(10, false, 'a', 11.5);

是:

void (*pFunc)(int&&, bool&&, char&&, double&&) = 
    &Factory::testFunc<int, bool, char, double>;

由于testFunc定义了一整套函数,因此您不能只使用一个函数指针来指向所有函数。你必须说出你正在使用函数指针的那个。

答案 2 :(得分:1)

你可以做到。你只需要:

  1. 实例化静态函数并获取其地址(testFunc只是一个模板(真正意义上的单词定义),您需要使用该模板来构建一个真实函数(这是<>所做的;它指定了编译器使用模板构建实际函数所需的所有部分)
  2. 创建一个带有正确参数的函数指针(记住,你在模板函数中使用&&
  3. Here's a demo

    #include <iostream>
    
    // Ignore this printHelper stuff; it just prints out the arguments
    static void printHelper()
    {
    }
    
    template <typename T>
    static void printHelper(T&& arg)
    {
        std::cout << arg;
    }
    
    template<typename T, typename ...Args>
    static void printHelper(T&& arg, Args&& ...args)
    {
        std::cout << arg << ", ";
        printHelper(args...);
    }
    
    class Factory
    {
    public:
        template<typename ...Args>
        static void testFunc(Args&& ...args)
        {
            std::cout << "inside function; args are: ";
            printHelper(args...);
            std::cout << std::endl;
        }
    };
    
    int main()
    {
        void (*pFunc1)() = &Factory::testFunc<>;
        pFunc1();
    
        void (*pFunc2)(int&&, char&&, double&&) = &Factory::testFunc<int, char, double>;
        pFunc2(1, 'a', 3.14);
    }
    

答案 3 :(得分:1)

如果你只想要一个指向一个模板函数实例的函数指针的指针,可以用这些参数调用它们:

void(*pFunc)(WHAT_TO_TYPE_HERE?) = &Factory::testFunc;
pFunc(10, false, 'a', 11.5);

然后WHAT_TO_TYPE_HERE变为

void(*pFunc)(int&&, bool&&, char&&, double&&) = &Factory::testFunc;
pFunc(10, false, 'a', 11.5);

现在,一个缺点是我们已经修复了函数指针中每个参数的rvalue / lvalue状态,​​这很烦人。

所以这个助手很有用:

template<class Sig, class Function>
struct testfunc_as_function_ptr;
template<class R, class...Args>
struct testfunc_as_function_ptr {
  R(*)(Args...) operator()() const {
    return [](Args...args){ return Factory::testFunc(std::forward<Args>(args)...); };
  }
};

这是非常迟钝的,但确实会给你:

void(*pFunc)(int,bool,char,double) = testfunc_as_function_ptr<void(int, bool, char, double)>{}();

您可以选择签名,如果签名与调用testFunc兼容,则可以使用。

但是,如果你想要一个指向ENTIRE模板函数的指针,而不仅仅是它的一个实例:

这是nary调度问题。

要意识到的第一件事是模板不是&#34;事物&#34;在C ++中。模板制作的东西。模板类使类 - 模板函数生成函数。

功能模板不是功能。它是生成函数的模板。

所以你的template<class...Args> void testFunc(Args&& ...args)不是一个功能。它是关于如何产生函数的指令 - 事实上,指令如何产生任意数量的不同函数。这些功能都是&#34;重载&#34;彼此。

对于函数,当你调用它们时,从参数自动生成重载,然后重载决策启动(可能会消除生成的重载,如果例如有一个非模板生成的函数也匹配)。

指针(主要)是运行时构造,指向函数的指针必须指向实际的&#34;&#34; (实际功能)。它不能指向模板。

现在,void testFunc(...)是一个实际的功能。无论传递给它的参数是什么,都会创建并运行相同的函数。所以你可以指向void testFunc(...)

因此,虽然我们不能有指向模板的指针,但您可以创建指向可以分派到模板的构造(对象)的指针。但是,这要求在创建构造时确定调度,并且调用该构造只能到达那些每个确定的实例。

这是一种nary dispatch - 你调用的重载由你传递给函数的参数决定。

C ++不支持在对象中存储代码生成模板。您可以将其存储在类型(类)中,但不能存储在实际对象中。

我们经常可以通过认识到模板函数的主体可能不需要关于Args...的所有内容来解决这个问题。例如,模板函数的主体可能只需要学习如何将给定的Arg转换为字符串?

在这种情况下,我们可以创建一个如下所示的界面:

struct printable { virtual std::string to_string() const = 0; ~printable(); };

using printer = void(*)(std::vector<std::unique_ptr<printable>>);

printer是一个指向vector printableT指针的函数的指针。然后,我们可以编写采用任意类型printable并使其成为Args...的代码。

这需要知道我们试图产生的功能需要了解boost::any

您可以将函数指针作为指向类型擦除的可迭代范围的指针,等同于void (*)(...),然后让主体确定实际需要的类型并进行内省。这是类型安全的等效于{{1}} C风格的变量,其中被调用的函数必须执行所有类型体操以确保手动工作。

答案 4 :(得分:1)

您的问题是&Factory::testFunc指的是重载集,而不是单个函数。

过载集是否包含(或包含)模板函数是不重要的,歧义意味着编译器不知道该怎么做。

只需static_cast指向所需类型的函数(或在另一个上下文中使用它,使所需类型明显),如下所示:

void(*pFunc)() = &Factory::testFunc;
f( (void(*)())&Factory::testFunc); // static_cast to get the right overload

见coliru:http://coliru.stacked-crooked.com/a/13571ddf095df15f

当然,因为它是一个模板函数,你可以通过显式指定参数来从过载集中选择一个函数,如下所示:

void(*pFunc)() = &Factory::testFunc<>;