Я знаю, что следующий код не скомпилируется.
void baz(int i) { }
void baz() { }
class Bar
{
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = baz) : bazFn(fun){}
};
int main(int argc, char **argv)
{
Bar b;
return 0;
}
Потому std::function
что сказано не учитывать разрешение перегрузки, как я читал в этом другом посте .
Я не совсем понимаю технические ограничения, которые вынудили такое решение.
Я читал об этапах перевода и шаблонах на cppreference, но я не могу придумать никаких рассуждений, к которым я не смог найти контрпример. Объяснили полу-дилетанту (все еще новичку в C ++), что и на каком этапе трансляции вышеописанное не компилируется?
c++
templates
translation
std-function
TuRtoise
источник
источник
Ответы:
Это на самом деле не имеет ничего общего с «фазами перевода». Это чисто о конструкторах
std::function
.Видите,
std::function<R(Args)>
не требует, чтобы данная функция была именно такого типаR(Args)
. В частности, для этого не требуется указатель функции. Он может принимать любой вызываемый тип (указатель на функцию-член, некоторый объект, имеющий перегрузкуoperator()
), при условии, что он вызывается, как если бы он принялArgs
параметры и возвратил что-то конвертируемое вR
(или, еслиR
естьvoid
, он может вернуть все что угодно).Чтобы сделать это, соответствующий конструктор
std::function
должен быть шаблон :template<typename F> function(F f);
. То есть он может принимать любой тип функции (с учетом вышеуказанных ограничений).Выражение
baz
представляет набор перегрузки. Если вы используете это выражение для вызова набора перегрузки, это нормально. Если вы используете это выражение в качестве параметра для функции, которая принимает указатель на конкретную функцию, C ++ может уменьшить перегрузку, установленную для одного вызова, что делает его нормальным.Однако, как только функция является шаблоном, и вы используете вывод аргумента шаблона, чтобы выяснить, что это за параметр, C ++ больше не имеет возможности определять, какова корректная перегрузка в наборе перегрузок. Поэтому вы должны указать это напрямую.
источник
function
шаблоне класса не имеет значения . Важным является параметр шаблона в конструкторе, который вы вызываете. Который простоtypename F
: ака, любой тип.Разрешение перегрузки происходит только тогда, когда (а) вы вызываете имя функции / оператора или (б) приводите его к указателю (к функции или функции-члену) с явной подписью.
Здесь ничего не происходит.
std::function
берет любой объект, который совместим с его подписью. Это не берет указатель функции определенно. (лямбда не является функцией std, а функция std не является лямбда)Теперь в моих вариантах доморощенных функций для подписи
R(Args...)
я также принимаюR(*)(Args...)
аргумент (точное совпадение) именно по этой причине. Но это означает, что он поднимает сигнатуры «точного соответствия» над «совместимыми» сигнатурами.Основная проблема заключается в том, что набор перегрузки не является объектом C ++. Вы можете назвать набор перегрузки, но вы не можете передать его «изначально».
Теперь вы можете создать псевдо-перегрузочный набор функции следующим образом:
это создает один объект C ++, который может выполнять разрешение перегрузки для имени функции.
Расширяя макросы, мы получаем:
что раздражает писать. Более простая, но чуть менее полезная версия здесь:
у нас есть лямбда, которая принимает любое количество аргументов, а затем идеально передает их
baz
.Затем:
работает. Мы откладываем разрешение перегрузки в лямбду, которую мы храним в
fun
, вместоfun
прямой передачи набора перегрузки (который он не может разрешить).По крайней мере, было предложено определить операцию на языке C ++, которая преобразует имя функции в объект набора перегрузки. Пока такое стандартное предложение не входит в стандарт,
OVERLOADS_OF
макрос полезен.Вы можете пойти еще дальше и поддержать приведение к совместимой функции-указатель.
но это начинает становиться тупым.
Живой пример .
источник
Проблема здесь заключается в том, что компилятору ничего не говорится о том, как сделать функцию для затухания указателя. Если у тебя есть
Тогда код будет работать, так как теперь компилятор знает, какую функцию вы хотите, поскольку есть конкретный тип, который вы назначаете.
При использовании
std::function
вы вызываете его конструктор объекта функции, который имеет формуи так как это шаблон, он должен определить тип передаваемого объекта. Поскольку
baz
это перегруженная функция, нет единого типа, который можно вывести, поэтому выведение шаблона завершится неудачно, и вы получите ошибку. Вы должны использоватьполучить силу одного типа и разрешить удержание.
источник
В тот момент, когда компилятор решает, какую перегрузку передать в
std::function
конструктор, все, что он знает, это то, чтоstd::function
конструктор имеет шаблон любого типа. У него нет возможности попробовать обе перегрузки и обнаружить, что первый не компилируется, а второй - компилируется.Чтобы решить эту проблему, нужно явно указать компилятору, какую перегрузку вы хотите, с помощью
static_cast
:источник