C++ Как ссылаться на шаблонные функции с помощью std::bind/std::function

Если у вас есть шаблонный класс или шаблонная функция (или их комбинация), как вы привязываете эту функцию (сохраняя параметр типа шаблона)?

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

Можно ли заставить это работать, чтобы по-прежнему можно было предоставлять параметры типа шаблона при будущих вызовах?

Много почистил этот код, но он, очевидно, не скомпилируется, потому что я не могу найти правильный синтаксис (есть ли способы сделать это)?

Удалено требование «вектор», чтобы упростить это:

Спасибо за помощь!

#include <functional>
#include <vector>
#include <string>

/***************************************/
template <typename CommandTemplateType>
class Storage
{
  public:
   // No idea how to define this vector to allow Template Parameters
   // static std::vector<std::function<void<ParameterTemplateType>
   //     (std::shared_ptr<ParameterTemplateType>)>> Functions;

   // I really don't need the collection, a single member would kick start my research:
   static std::function<void<ParameterTemplateType>(std::shared_ptr<ParameterTemplateType>)> Function;

  template <typename ParameterTemplateType>
  static void Execute(ParameterTemplateType parameter)
  {
     // Look up index, or loop through all.. 
     // I am trying to invoke the bound function with a template param:
     // Functions[index]<ParameterTemplateType>(parameter);
     // preferably, just:  
     Function<ParameterTempalteType>(parameter); 
  }
};

/***************************************/
template <typename TemplateType>
class MyClass
{

   template <typename ParameterTemplateType>
   void MyFunction(ParameterTemplateType myParameter)
   {
     // Do something; 
   }

   MyClass()
   {
      std::string parameter = L"Test String";

      // Do not know how to include the 
      // template<typename ParameterTemplateType> definition to bind call.
      // Storage::Functions.push_back(
      //     std::bind(&MyClass::MyFunction<ParameterTemplateType>,
//        this, std::placeholders::_1));

     // Or just something like:
     Storage::Function = std::bind(&MyClass::MyFunction<ParameterTemplateType>,
                             this, std::placeholders::_1));

      /***************************************/
      // Call the bound function with an explicit parameter somehow:
      std::string parameter = L"Test String";          
      Storage::Execute<std::string>(parameter);


   }
};

person Community    schedule 06.02.2013    source источник
comment
Что вы хотите передать как myParameter?   -  person Joseph Mansfield    schedule 06.02.2013
comment
Примечание по терминологии: не существует такого понятия, как «шаблонный класс/функция». Существуют шаблоны классов и шаблоны функций. Это довольно фундаментальное отличие: дело не в том, что что-то («шаблон», что бы это ни было) применяется к классу. Наоборот: шаблон класса сам по себе является шаблоном — моделью или шаблоном, из которого можно построить конкретный класс (путем вставки аргументов шаблона). Думайте о шаблоне класса как о форме, из которой отливается меч (= класс), а не как о мече с какими-то украшениями на нем.   -  person Konrad Rudolph    schedule 06.02.2013
comment
Если я правильно понимаю, о чем вы спрашиваете, это невозможно. std::bind — это концепция времени выполнения, возвращающая (по сути) функцию, которую можно вызвать. Создание экземпляра шаблона функции (заполнение параметра шаблона) должно выполняться во время компиляции. Вы не можете выполнить привязку к функции, которая (может быть) все еще нуждается в компиляции...   -  person Chris Hartman    schedule 17.02.2013
comment
Я понял, как это сделать. Вы должны обернуть шаблонную функцию в какой-то шаблонный контейнер. Я разместил ответ ниже. Спасибо всем за помощь!   -  person e.s. kohen    schedule 17.02.2013


Ответы (4)


Ключевая проблема заключается в том, что в С++ 11 вы не можете сделать что-то вроде:

// Doesn't compile
template <typename TemplateType>
static std::function<void(std::shared_ptr<TemplateType>)> Function;

Классы и функции могут быть шаблонными, но не свойства членов.

"Магия" это:

/*******************************************************************/
// Define a Function Pointer in a Container
class Storage
{
   template <typename TemplateType>
   struct FunctionContainer {
       static std::function<void(std::shared_ptr<TemplateType>)> Function;
   };
};
/*******************************************************************/
// Initialize FunctionContainer's Static Function Pointer if using static pointer.
template <typename TemplateType>
std::function<void(std::shared_ptr<TemplateType>)> Storage
    ::FunctionContainer<TemplateType>::Function;

Затем вы можете привязать шаблонную функцию к этой функции, например:

// Bind Function Pointer in Container to a Local Function
class MyClass
{
   template <typename TemplateType>
   void MyFunction(std::shared_ptr<TemplateType> parameter)
   {
     // Do something.
     // You can make this templated or non-templated.
   }
   MyClass()
   {
     // If you really want, you can templatize std::string in the following:
     Storage::FunctionContainer<std::string>::Function 
       = std::bind(&MyFunction<std::string>, this, std::placeholders::_1);
   }
}

И вы можете вызвать все это и предоставить параметр шаблонного типа следующим образом:

//Invocation
std::shared_ptr<std::string> parameter;
parameter->get() = "Hello World".
Storage::FunctionContainer<std::string>::Function(parameter);
person Community    schedule 16.02.2013
comment
Никогда раньше не видел такой конструкции: parameter->get() = "Hello World". что это делает? - person Gabriel; 20.12.2013
comment
Привет! std::shared_ptr‹›.get() — это просто метод C++ API, который является просто ссылкой на указатель std:shared_ptr (cplusplus.com/reference/memory/shared_ptr) ... Просто присвоение значения строки Hello World в качестве значения общего указателя. Я давно не редактировал свой вопрос, чтобы очистить его, и, похоже, я должен это сделать! Спасибо! - person e.s. kohen; 20.12.2013

Аргумент шаблона для std::function должен быть сигнатурой функции после подстановки типа шаблона. В вашем случае ни TemplateType, ни FunctionTemplateType не влияют на подпись функции-члена MyFunction - она ​​всегда будет возвращать std::string и принимать один аргумент std::string. Следовательно, std::function, которое вы собираетесь хранить в std::vector, должно быть:

static std::vector<std::function<std::string(std::string)>> Functions;

Напомним, что функция-член имеет неявный первый аргумент this. Вам нужно привязать первый аргумент MyClass<...>::MyFunc<...> к объекту, для которого вы хотите, чтобы он вызывался. Предположительно, поскольку вы привязываете функцию в конструкторе MyClass, вы хотите, чтобы объект был экземпляром MyClass. Это означает, что ваш push_back должен выглядеть так:

Storage::Functions.push_back(
  std::bind(&MyClass<TemplateType>::MyFunction<int>, this,
    std::placeholders::_1)
);

Теперь функция, помещенная в Functions, связана с вашим объектом MyClass и принимает один аргумент типа std::string. Вы можете вызвать одну из этих функций следующим образом:

Storage::Functions[0]("something");
person Joseph Mansfield    schedule 06.02.2013
comment
Благодарю вас! Могу ли я вызвать это из-за пределов класса? Например, если я знаю параметр: class Outside { void Do(CustomClass * stuff) { Storage::Functions[найти правильную подпись](stuff);}}; - person e.s. kohen; 06.02.2013
comment
Что, если я вставлю эту функцию в вектор изнутри функтора, используя что-то вроде Storage::Functions.push_back(std::bind( this ..... // ) - person e.s. kohen; 06.02.2013
comment
@WindAndFlame MyClass<TemplateType>::MyFunction<anything> — это экземпляр вашей функции. & (адрес) дает вам указатель на функцию-член, аналогичный указателю функции, но функция, на которую он указывает, должна вызываться для определенного объекта. Функции-члены всегда вызываются для объекта, если только они не являются статическими. - person Joseph Mansfield; 07.02.2013
comment
извините, ‹что угодно› ... это ключевое слово? Не видя, как указатель знает о типе параметра.. - person e.s. kohen; 07.02.2013
comment
@WindAndFlame Теперь, что bind делает с этой функцией? Все аргументы, следующие за указателем на функцию-член, являются аргументами, которые должны быть привязаны к аргументам этой функции. Ваш MyFunction имеет два аргумента: первый — это неявный аргумент this, который есть у каждой функции-члена, а второй — std::string. Итак, мы привязываем текущий объект this к первому аргументу и, используя заполнитель, говорим, что другой аргумент (std::string следует оставить несвязанным. Теперь функция, которую дает вам bind, принимает только один аргумент, std::string. - person Joseph Mansfield; 07.02.2013
comment
@WindAndFlame <anything> - это то же самое <anything>, которое я дал в своем ответе. В вашем исходном вопросе был FunctionTemplateType, который ни на что не повлиял. Я просто сказал, что вы можете указать любой тип для этого - anything не является чем-то конкретным. Это может быть int, Foo, что угодно. - person Joseph Mansfield; 07.02.2013
comment
давайте продолжим это обсуждение в чате - person Joseph Mansfield; 07.02.2013
comment
Вы упомянули: std::bind(&MyClass‹TemplateType›::MyFunction‹int›, this, std::placeholders::_1) .. Что, если я хочу указать TemplateType, но хочу оставить MyFunction‹int› по-прежнему шаблонным , чтобы функцию по-прежнему можно было вызывать с помощью шаблона? Функции[0]‹std::string›(MyString); ? Существуют ли std::placeholders::_? для параметров шаблона? - person e.s. kohen; 07.02.2013
comment
Все еще пытаюсь понять, как сделать что-то вроде: Storage::Functions[0]‹std::string›(something); - person e.s. kohen; 12.02.2013

Если я вас правильно понял... :)

То, что вы хотите сделать, невозможно, потому что для шаблона‹class T› void foo(T) функции foo‹int›() и foo‹double› имеют разные типы, и вы не можете создавать векторные указатели на обе эти функции напрямую, потому что вектор является однородным контейнером.

Чтобы преодолеть это, мы можем использовать boost::variant‹> либо для хранения указателей на различные типы функций, либо для хранения аргументов функций.

template<class T> void foo(T);
typedef boost::variant<void (*)(int), void (*)(double)> func_ptr_variant;
std::vector<func_ptr_variant> v;
v.push_back(foo<int>);
v.push_back(foo<double>);

typedef boost::variant<int, double> argument;
std::vector<void (*)(argument)) v;
v.push_back(foo);
v.push_back(bar);
// foo and bar are defined as void foo(argument a) and void bar(argument a)

К сожалению, в любом случае вам нужно будет создавать экземпляры шаблонов функций перед их вставкой в ​​контейнер, потому что C++ не может генерировать код на лету. Я думаю, что вполне возможно, что вы знаете все возможные типы аргументов, с которыми может использоваться функция, так что это может не быть проблемой.

person Community    schedule 16.02.2013
comment
Как насчет того, чтобы не использовать вектор? Я закомментировал все ссылки на вектор .. - person e.s. kohen; 16.02.2013
comment
Просто сохраните один вариант со всеми возможными экземплярами функций. - person Manvel; 16.02.2013

c-tor MyClass ничего не знает о FunctionTemplateType, поэтому он может push_back только явно специализированный (извините, это мой термин... я не знаю правильный термин) вот так

#include <functional>
#include <vector>
#include <string>

struct Storage
{
  // Have no idea what this signature should really be:
  static std::vector<std::function<void ()>> Functions;

};
std::vector<std::function<void ()>> Storage::Functions;

template <typename TemplateType>
class MyClass
{
   template <typename FunctionTemplateType>
   std::string MyFunction(std::string myParameter)
   {
     return "Hellö: " + myParameter;

   }
public:
   MyClass()
   {
      Storage::Functions.push_back(
          std::bind( & MyClass<TemplateType>::MyFunction<std::string>, this, "borisbn" )
//                                                       ^^^^^^^^^^^
      );
   }
};

int main() {
    MyClass<int> obj;
}

ссылка на рабочее пространство

person borisbn    schedule 06.02.2013