2015-08-17 5 views
2

У меня есть проблема с передачей параметров const ref для функций шаблона, вызывающих другие функции. Рассмотрим следующий код:Параметр аргумента аргумента функции неоднозначный для типов const ref

struct A 
{ 
    void foo(const int& i) { } 
}; 

template <class ...Args> 
void a_caller(A& a, void(A::*f)(Args...), Args&& ...args) 
{ 
    (a.*f)(std::forward<Args>(args)...); 
} 

int main() 
{ 
    int i = 42; 
    A a; 

    a_caller(a, &A::foo, i); // (1) compiler error 
    a_caller<const int&>(a, &A::foo, i); // (2) ok 
} 

Итак, у меня есть функция члена A::foo с const int& аргумент, который я хочу, чтобы позвонить в обертке a_caller. Линия (1) вызывает следующую ошибку:

'void a_caller(A &,void (__thiscall A::*)(Args...),Args &&...)' : template parameter 'Args' is ambiguous 
see declaration of 'a_caller' 
could be 'const int&' 
or  'int&' 

Мой первый вопрос, почему это происходит? Я даю компилятору неперегруженную функцию A :: foo, почему он не может вывести из нее Args? Второй вопрос: почему этого не происходит для std :: make_unique? Следующий код выглядит так же для меня, но компилятор не имеет никаких проблем выводя конструктор типа аргумента:

struct A 
{ 
    A(const int& i) { } 
}; 

int main() 
{ 
    int i = 42; 
    auto aptr = std::make_unique<A>(i); 
} 

ответ

6

Вы пытаетесь впихнуть Args в выполнении двух различных (и не обязательно совместимы) роли. Первой ролью является тип параметров f. Второй - тип аргументов, заданных a_caller.

Благодаря совершенно экспедирование осуществляется, проходя i как в вашем примере хочет вывести Args типа для этого i в int &. Однако тот же тип Args в A::foo имеет тип const int & —, следовательно, двусмысленный вывод.

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

template <class ...Params, class ...Args> 
void a_caller(A& a, void(A::*f)(Params...), Args&& ...args) 
{ 
    (a.*f)(std::forward<Args>(args)...); 
} 

Вы должны полагаться на призыванием f говорит вам, когда аргументы не соответствуют параметрам.

1

Сообщение об ошибке говорит вам, что происходит

see declaration of 'a_caller' 
could be 'const int&' 
or  'int&' 

Таким образом, вы передаете функцию-член, которая принимает const int& поэтому компилятор выводит что Args как const int& но вы также передать i для Args которой выводящей в a int&. Эти конфликты, чтобы вы получили ошибку. Вы можете const_casti и что будет компилировать или вы могли бы пройти const int в качестве второго параметра

a_caller(a, &A::foo, const_cast<const int&>(i)); 
const int foo = 42; 
a_caller(a, &A::foo, foo); 
1

Мой первый вопрос, почему это происходит? Я даю компилятору неперегруженную функцию A :: foo, почему он не может вывести Args из нее?

Поскольку вы пытаетесь вывести Args дважды, для первого и второго параметров функции a_caller. И эти выведенные типы не совпадают, const int& для первого параметра и int& для второго параметра.

Второй вопрос: почему этого не происходит для std :: make_unique?

Поскольку make_unique только передает свои аргументы конструктору класса.

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

#include <memory> 

struct A 
{ 
    void foo(const int& i) { } 
}; 

template <typename F, class ...Args> 
void a_caller(A& a, F &&f, Args&& ...args) 
{ 
    (a.*f)(std::forward<Args>(args)...); 
} 

int main() 
{ 
    int i = 42; 
    A a; 

    a_caller(a, &A::foo, i); 
} 

DEMO

+0

Спасибо за подсказку с указателем метода в качестве шаблона PARAM - выглядит короче, что предложение Angew (за исключением случаев, когда возврат тип метода не важен) – Lao