У меня есть класс с функцией 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
Вы можете использовать лямбда:
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);
Как 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.
Проблема в том, что нестатические функции-члены имеют скрытый, неявный this
параметр, указывающий на экземпляр, который его вызвал. Ваш компилятор прав отклонить его, поскольку он не имеет правильных аргументов для функции. Отправка this
в качестве дополнительного параметра привязки будет работать:
myPool.add(&MyClass::doIt, this, a);
// ^^^^
Использование выражения lamda также будет работать.
Кроме того, стандартная функция библиотеки std::async()
уже делает то, что вы пытаетесь сделать здесь, и многое другое. Подумайте об использовании этого вместо этого.
Изменить: вам также нужно указать правильный тип, чтобы выбрать правильную перегрузку. @Jarod42 уже показывает вам, как это сделать.
this
не решит проблему перегрузки .
this
.