У нас есть обычная иерархия классов:
class B
{
public:
int x;
B() : x(0) {}
virtual ~B() {}
};
class D : public B
{
public:
int y;
D() : y(0) {}
};
И функция, которая принимает один аргумент — ссылку на объект базового класса.
void b_set(B& b)
{
b.x = 5;
}
Затем я хочу создать указатель на функцию типа void (D&)
и хранить b_set
в этом. Это должна быть допустимая операция, так как все объекты, законно переданные для вызова указателя функции, также должны иметь тип B. Однако это не разрешено.
typedef void (*fp_d_mutator)(D&);
void fp_test(D& obj)
{
fp_d_mutator fun = b_set; //invalid conversion from 'void (*)(B&)' to 'fp_d_mutator {aka void (*)(D&)}
fun(obj);
}
#include <functional>
typedef std::function<void (D&)> stdfun_d_mutator;
void stdfun_test(D& obj)
{
stdfun_d_mutator fun = b_set; //works
fun(obj);
}
Так…
Функция, которая принимает аргумент типа B&
не является функцией, которая принимает аргумент типа D&
, В то время как D&
является конвертируемый в B&
они не одного типа. Если бы вы могли хранить указатель на функцию, которая принимает B&
как указатель на D&
как компилятор узнает, когда нужно преобразовать аргумент? (Обратите внимание, что преобразование иногда требует настройки указателя)
Разница в std::function
является то, что вызывающая подпись (здесь D&
) является частью типа объекта функции, а также вызываемая подпись (здесь B&
) является частью внутреннего хранилища. Поэтому, когда вы применяете функциональные объекты operator()
код, который реализует operator()(D&)
заботится о преобразовании.
Других решений пока нет …