Параметр шаблона аргумента функции неоднозначен для константных типов ссылок

У меня проблема с передачей параметров 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);
}

2

Решение

Вы пытаетесь рожок для обуви 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 говорю вам, когда аргументы не соответствуют параметрам.

6

Другие решения

Мой первый вопрос: почему это происходит? Я даю компилятору не перегруженную функцию 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

1

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

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);
1
По вопросам рекламы [email protected]