Передать перегруженную функцию-член в функцию шаблона

0

У меня есть класс с функцией add:

class Pool {
public:
    Pool() {};
    template<class F, class... A>
    auto add(F&& f, A&&... args) -> std::future<typename std::result_of<F(A...)>::type>
    {
        // return empty placeholder, for the sake of this code example
        std::future<typename std::result_of<F(A...)>::type> ret;
        return ret;
    };
};

Он должен принимать любую функцию с ее аргументами, добавлять ее в пул потоков и возвращать будущее типа результата этой функции.

И класс, в котором я его использую:

class MyClass {
public:
    string doIt(string) { return string("a"); };
    string doIt(int, string) { return string("b"); };

    void test() {
        Pool myPool;
        string a("test");
        myPool.add(&MyClass::doIt, a); // Error
    };
};

Что дает ошибку компилятора:

Error   1   error C2914: 'Pool::add' : cannot deduce template argument as function argument is ambiguous    MyClass.cpp 94

Теперь проблема (я думаю), что компилятор не может определить, какую перегрузку я хочу использовать. Подобно функции Overloaded в качестве аргумента функции вариационного шаблона. (Также я не на 100% понятен, почему я должен использовать "&" для функций класса, но без амперсанда, если я передаю свободную функцию). Во всяком случае, я также пробовал обходное решение, упомянутое выше:

struct doIt_wrapper {
    template <typename... T>
    auto operator()(T... args) -> decltype(doIt(args...)) {
        return doIt(args...);
    }
};

а затем измените MyClass :: test() на:

    void test() {
        Pool myPool;
        string a("test");
        myPool.add(doIt_wrapper(), a);
    };

Но это также дает мне ошибку компилятора:

error C2893: Failed to specialize function template 'unknown-type doIt_wrapper::operator ()(T...)'  C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xrefwrap 58

Я также пробовал несколько вариантов, таких как myPool.add(doIt_wrapper<string>() и с/без '&', но все они генерируют одну или другую ошибку компилятора.

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

Изменение: Исправлено несколько опечаток и выгрузили минимальный пример: http://ideone.com/eX1r1l

Теги:
c++11
variadic-templates

3 ответа

1
Лучший ответ

Вы можете использовать лямбда:

myPool.add([this](const std::string& s) {doIt(s);}, a);

или даже

myPool.add([this, a]() {doIt(a);});

В настоящее время вы можете указать, какую перегрузку использовать таким образом:

myPool.add(static_cast<std::string (MyClass::*) (std::string)>(&MyClass::doIt), a);

Обратите внимание, что doIt - это метод (а не свободная функция или статическая функция), поэтому вам нужно вызвать его с помощью объекта. Если вы добавите static to doIt, вы можете выбрать перегрузку с помощью

myPool.add(static_cast<std::string (*) (std::string)>(&MyClass::doIt), a);
  • 0
    Первый актерский состав также должен связать this .
  • 0
    Спасибо за лямбду и решение по кастингу! Они действительно оба работают. Просто нужно сейчас решить, какой из них наименее уродлив, или, скорее, который показывает намерение самым ясным образом.
Показать ещё 1 комментарий
2

Как doIt_wrapper другие, проблема заключается в том, что doIt() не может быть doIt_wrapper внутри класса doIt_wrapper как ему также нужен указатель на вызываемый объект. Вы можете просто изменить doIt_wrapper operator() чтобы также взять указатель на объект и передать указатель на this как первый аргумент для add(). Тогда будет выглядеть примерно так:

#include <iostream>
#include <future>
using namespace std;

class Pool {
public:
    Pool() {};
    template<class F, class... A>
    auto add(F&& f, A&&... args) -> std::future<typename std::result_of<F&&(A&&...)>::type>
    {
        // return empty placeholder, for the sake of this code example
        std::future<typename std::result_of<F&&(A&&...)>::type> ret;
        return ret;
    };
};

class MyClass {
public:
     string doIt(string) { return string("a"); };
     string doIt(int, string) { return string("b"); };

     struct doIt_wrapper
     {
        template<class T, class... Ts>
        auto operator()(T&& t, Ts&&... args) -> decltype(t->doIt(std::forward<Ts>(args)...))
        {
           return t->doIt(std::forward<Ts>(args)...);
        }
     };

    void test() {
        Pool myPool;
        string a("test");
        myPool.add(doIt_wrapper(), this, a); // No error no more
    };
};

int main() {
   // your code goes here
   MyClass my;
   my.test();
   return 0;
}

Таким образом, вам не нужно делать броски. Код компилируется как для GCC, так и для Clang.

  • 0
    Большое спасибо за указание на это решение и за исправление моего кода-обертки! Полезно знать, как реализовать этот вариант. Хотя я склоняюсь к другим решениям.
1

Проблема в том, что нестатические функции-члены имеют скрытый, неявный this параметр, указывающий на экземпляр, который его вызвал. Ваш компилятор прав отклонить его, поскольку он не имеет правильных аргументов для функции. Отправка this в качестве дополнительного параметра привязки будет работать:

 myPool.add(&MyClass::doIt, this, a);
 //                         ^^^^

Использование выражения lamda также будет работать.

Кроме того, стандартная функция библиотеки std::async() уже делает то, что вы пытаетесь сделать здесь, и многое другое. Подумайте об использовании этого вместо этого.


Изменить: вам также нужно указать правильный тип, чтобы выбрать правильную перегрузку. @Jarod42 уже показывает вам, как это сделать.

  • 0
    Добавление this не решит проблему перегрузки .
  • 0
    @ Jarod42 Спасибо.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню