2014-11-21 3 views
4

У меня есть код:Указатель на переменную статическую функцию шаблона. Как?

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); 

PS: с многоточием (.. .) все работает.

+1

'testFunc' - это не функция, а шаблон функции. Поэтому указатель на функцию (член) не может указывать на 'testFunc'. – chris

+0

@chris Но он работает для указателя на бесконфликтную функцию. – nikitablack

+0

Если с (...) все работает, тогда в чем вопрос? – zoska

ответ

3

Возможно ли создать указатель на testFunc?

Декларация testFunc определяет семейство функций, параметризовано Args. Каждая из этих функций будет иметь свой собственный адрес памяти. Таким образом, вы можете иметь указатель только на одну из функций, определяемых testFunc, например testFunc<int, double> или testFunc<>.

Вместо того, чтобы писать следующее, чтобы получить указатель на 0-аргумента перегрузки testFunc

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

вы должны писать

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

потому Factory::testFunc без <> является набор перегрузки, не функция.

+0

Спасибо. Я вижу логику. Но в любом случае я не могу объявить указатель на функцию, например, ** int **. Это не компилируется: 'void (* pFunc) (int) = & Factory :: testFunc ; \t pFunc (10); ' – nikitablack

+1

@nikitablack Это потому, что подпись' pFunc' неверна. Это должно быть так: 'void (* pFunc) (int &&)' –

+0

@Timothy Shields Спасибо. – nikitablack

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 определяет целое семейство функций, вы не можете просто взять один указатель на функцию для всех из них. Вы должны сказать, к какому из них вы направляете указатель на функцию.

1

Вы можете это сделать.Вам просто нужно:

  1. Инстанцировать статическую функцию и принимает его адрес (testFunc просто шаблона (в реальном смысле определения этого слова), вы должны использовать этот шаблон для создания реального (это то, что делает <>, оно определяет все части, которые компилятор должен использовать для создания реальной функции)
  2. Сделать указатель функции, который принимает правильные аргументы (помните, вы используете && в функции шаблона)

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); 
} 
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 оно работает.

Однако, если вы хотите указатель на ВСЕЙ шаблонной функции, а не только его экземпляр:

Это проблема ни капли диспетчерская.

Первое, что нужно понять, это то, что шаблоны не являются «вещами» на C++. шаблоны сделать вещи. Класс template делает классы - функция-шаблон выполняет функции.

Функциональный шаблон не является функцией. Это шаблон для создания функций.

Таким образом, ваш template<class...Args> void testFunc(Args&& ...args) не является функцией. Это инструкции о том, как создать функцию - на самом деле, инструкции, как создавать любое количество различных функций. Эти функции являются «перегрузками» друг друга.

Для функций, когда вы их вызываете, перегрузка автоматически генерируется из аргументов, а затем выполняется перегрузка разрешения (возможно, исключая генерируемую перегрузку из рассмотрения, если, например, есть не-шаблонная функция, которая соответствует точно так же Что ж).

Указатель является (главным образом) конструкцией выполнения, а указатель на функцию должен указывать на фактическую «вещь» (фактическую функцию). Он не может указывать на шаблон.

Теперь, void testFunc(...) является фактической функцией. Эта же функция создается и запускается независимо от того, какие аргументы переданы ей.Таким образом, вы можете иметь указатель на void testFunc(...).

Так что, хотя мы не можем иметь указатель на шаблон, вы можете создать указатель на конструкцию (объект), которая может отправить шаблон. Это, однако, требует, чтобы отправка была определена в том месте, в котором была создана конструкция, и вызов этой конструкции может достигать только тех определенных экземпляров.

Это своего рода бесплатная отправка, которая перегружает вас, определяется аргументами, которые вы передаете функции.

C++ не поддерживает сохранение шаблона генерации кода в объекте. Вы можете сохранить его в типе (классе), но не в реальном объекте.

Мы можем часто исправить эту проблему, осознав, что для тела функции шаблона может вообще не понадобиться все, что касается Args.... Например, возможно, тело функции шаблона нужно только узнать, как превратить данный Arg в строку?

В этом случае мы можем создать интерфейс, который выглядит как:

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

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

и printer является указателем на функцию, которая принимает vector из printable указателей. Затем мы можем написать код, который принимает произвольный тип T и делает его printable.

Это требует знать, что о функции, которую мы пытаемся произвести, необходимо знать о Args....

Вы можете зайти так далеко, как указатель вашей функции быть указателем на итерируемый диапазон с типом, эквивалентным boost::any, а затем определить, какие типы на самом деле нужны и выполнять интроспекцию. Это эквивалент типа эквивалент void (*)(...) C-style variardic, где вызываемая функция должна выполнять все виды гимнастики, чтобы убедиться, что все работает вручную.

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<>; 
+0

Это действительно корректное поведение: 'void (* pFunc3) (std :: string &&, std :: string &&, int &&, int &&) = (void (*) (std :: string &&, std :: string &&, int &&, int &&)) & Factory :: testFunc; pFunc3 («Hello», «world», 42, 13); '??? Как компилятор знает, чтобы создать экземпляр 'Factory :: testFunc '? Выполняет ли сам экземпляр функцию? – Cornstalks

+0

@Cornstalks: Простой. Он выполняет свою нормальную функцию перегрузки-разрешения (именно так она определена), и единственным лучшим совпадением является создание шаблона. Это приведет к тому, что он будет использоваться ODR (если используется в потенциально оцениваемом контексте) и, таким образом, неявно создается экземпляр. – Deduplicator

+0

Так делает ли сам факт создания экземпляра? Наверное, я просто смущен тем, как создается эта инстанция, чтобы она могла играть роль в разрешении перегрузки. Во всяком случае, здесь есть +1 для обучения меня чему-то новому. – Cornstalks