Ковариантность и контравариантность в указателе функции C ++? - PullRequest
0 голосов
/ 19 сентября 2018

Рассмотрим это

class Base { };

class Derived : public Base { };

Base *f1(Derived *) { return {}; }
Derived *f2(Derived *) { return {}; }   // covariant
Base *f3(Base *) { return {}; } // contravariant
Derived *f4(Base *) { return {}; } // covariant & contravariant

using Callback = Base *(*)(Derived *);

Callback pfunc1 = f1;   // works of course

// These won't work...
Callback pfunc2 = f2;
Callback pfunc3 = f3;
Callback pfunc4 = f4;


// So I have to make a wrapper for each
Base *f2_wrap(Derived *d)
{
    return f2(d);
}

Base *f3_wrap(Derived *d)
{
    return f3(d);
}

Base *f4_wrap(Derived *d)
{
    return f4(d);
}

// Now it works
Callback pfunc2 = f2_wrap;
Callback pfunc3 = f3_wrap;
Callback pfunc4 = f4_wrap;

Так почему я не могу установить указатель на функцию, для которой в качестве возвращаемого значения указан объект Derived, или для функции, в которой в качестве объекта Baseаргумент (который работает в c # делегате)?

Я знаю, что могу обойтись с помощью функции-оболочки, но почему она не является частью языковой функции?

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Необработанные указатели функций совместимы только по присваиванию, когда типы точно совпадают.

Однако вы можете использовать std::function:

#include <functional>

struct Base{};
struct Derived: Base{};

auto f1( Derived* ) -> Base*        { return 0; }
auto f2( Derived* ) -> Derived*     { return 0; }   // covariant
auto f3( Base* )    -> Base*        { return 0; }   // contravariant
auto f4( Base* )    -> Derived*     { return 0; }   // covariant & contravariant

auto main()
    -> int
{
    using Callback = std::function<auto( Derived* ) -> Base*>;

    Callback pfunc1 = f1;   // works
    Callback pfunc2 = f2;   // works
    Callback pfunc3 = f3;   // works
    Callback pfunc4 = f4;   // works
}

Правила для переопределения виртуальных функций менее строгие:ковариантные результаты необработанного указателя и ссылочного типа поддерживаются, но это все.Нет различий.

#include <functional>

struct Base{};
struct Derived: Base{};

struct F{ virtual auto f( Derived* ) -> Base* = 0; };

#define R override { return 0; }
struct F1: F { auto f( Derived* ) -> Base*      R };
struct F2: F { auto f( Derived* ) -> Derived*   R };   // covariant, OK
struct F3: F { auto f( Base* )    -> Base*      R };   // !contravariant
struct F4: F { auto f( Base* )    -> Derived*   R };   // !covariant & contravariant

Результат компиляции с MinGW g ++ 7.3.0:

> g++ -c 2.cpp
2.cpp:11:21: error: 'Base* F3::f(Base*)' marked 'override', but does not override
 struct F3: F { auto f( Base* )    -> Base*      R };   // !contravariant
                     ^
2.cpp:12:21: error: 'Derived* F4::f(Base*)' marked 'override', but does not override
 struct F4: F { auto f( Base* )    -> Derived*   R };   // !covariant & contravariant

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

0 голосов
/ 19 сентября 2018

C ++ был разработан для работы таким образом, чтобы не принимать другой параметр., Даже если это производный класс.Взгляните на этот поток Указатели на функции приведения, которые отличаются типом аргумента

C # имеет виртуальную машину и не допускает множественное наследование, поэтому C # имеет гораздо больше контроля в такой ситуации, вы не можете сравнивать C # и C ++.

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

class Base { };

class Derived : public Base { };

Base *f1(Derived *) { return {}; }
Derived *f2(Derived *) { return {}; }   // covariant
Base *f3(Base *) { return {}; } // contravariant
Derived *f4(Base *) { return {}; } // covariant & contravariant

using Callback = Base *(*)(Derived *);

Callback pfunc1 = f1;   // works of course

// These won't work (now it will work)...
Callback pfunc2 = (Callback)(f2); //explict cast
Callback pfunc3 = (Callback)f3;
Callback pfunc4 = (Callback)f4;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...