У меня проблема с передачей параметров 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);
}
Вы пытаетесь рожок для обуви 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
говорю вам, когда аргументы не соответствуют параметрам.
Мой первый вопрос: почему это происходит? Я даю компилятору не перегруженную функцию 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);
}
Сообщение об ошибке говорит вам, что происходит
see declaration of 'a_caller'
could be 'const int&'
or 'int&'
Итак, вы передаете функцию-член, которая принимает const int&
поэтому компилятор выводит, что Args
как const int&
но вы также проходите i
за Args
который выводит как int&
, Эти конфликты, так что вы получите ошибку. Вы могли бы const_cast
i
и это скомпилируется, или вы могли бы передать const int
как второй параметр
a_caller(a, &A::foo, const_cast<const int&>(i));
const int foo = 42;
a_caller(a, &A::foo, foo);