Различие guish между ссылками / указателями на функции, принимающими константные и неконстантные аргументы с тем же именем, что и у параметра функции - PullRequest
7 голосов
/ 24 января 2020

Давайте рассмотрим две функции с одинаковыми именами:

int func(int&)
{
    return 7;
}
int func(const int&)
{
    return 5;
}

Пусть где-нибудь определится int mutableValue = 5. Есть ли вероятность, что шаблонная функция call в call(mutableValue, func) займет всего int func(int&)? Я не хочу использовать static_cast<int(&)(int&)> - потому что он очень шумный.

Наивная реализация:

template<typename Arg, typename Ret>
void call(Arg& val, Ret (&fun)(Arg&))
{
    fun(val);
}

работает на g cc, но не работает на clang - решения должны работать на обоих компиляторах.

Принимать только const Arg& легко. Удаление const Arg& перегрузки не помогает.

Ответы [ 2 ]

4 голосов
/ 24 января 2020

Я считаю, что Clang - единственный компилятор, который делает это правильно. Это не должно компилироваться. Пока аргумент fun является набором перегрузки, который не содержит шаблонов функций, для каждого члена набора перегрузки будет предприниматься попытка выведения аргумента шаблона. Если вывод аргумента шаблона будет успешным для более чем одной из этих перегрузок, параметр функции станет неотчуждаемым контекстом [temp.deduct.call] /6.2.

В рассматриваемом примере аргументом fun является набор перегрузки func, который действительно не содержит шаблонов функций. Таким образом, для обоих перегрузок func предпринимается попытка выведения аргумента для fun, что успешно. В результате, параметр fun становится не выводимым контекстом, что означает, что для Ret не может быть выведен аргумент, и вызов завершается неудачно, так как нет кандидатов (именно на что жалуется clang).

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

call<int>(mutableValue, func)
2 голосов
/ 25 января 2020

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

Если вы согласны с изменением синтаксиса на сайте вызовов, вы можете разделить вызов на два прохода вызовов / удержаний :

template<typename Arg>
auto call(Arg& val)
{
    return [&](auto (&fun)(Arg&)){ fun(val); };
}

будет называться

call(mutableValue)(func)

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

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

...