Ссылка на интерфейс для локальной реализации - PullRequest
4 голосов
/ 07 декабря 2010

Пожалуйста, рассмотрите следующий код:

struct A
{
    virtual ~A() {}
    virtual int go() = 0;
};

struct B : public A { int go() { return 1; } };

struct C : public B { int go() { return 2; } };

int main()
{
    B b;
    B &b_ref = b;

    return b_ref.go();
}

В соответствии с GCC 4.4.1 (с использованием -O2), вызов B::go() становится встроенным (т. Е. Виртуальная отправка не происходит).Это означает, что компилятор признает, что a_ref действительно указывает на переменную типа B.Ссылка B может использоваться для указания на C, но компилятор достаточно умен, чтобы предвидеть, что это не так, поэтому он полностью оптимизирует вызов функции, вставляя функцию.

Отлично! Это невероятная оптимизация.

Но тогда почему GCC не делает то же самое в следующем случае?

struct A
{
    virtual ~A() {}
    virtual int go() = 0;
};

struct B : public A { int go() { return 1; } };

struct C : public B { int go() { return 2; } };

int main()
{
    B b;
    A &b_ref = b;

    return b_ref.go(); // B::go() is not inlined here, and a virtual dispatch is issued
}

Есть идеи?А как насчет других компиляторов?Этот вид оптимизации распространен?(Я очень новичок в этом понимании компилятора, поэтому мне любопытно)

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

// without static_ptr<>
void func(B &ref);

int main()
{
    B b;
    func(b); // since func() can't be inlined, there is no telling I'm not
             // gonna pass it a reference to a derivation of `B`

    return 0;
}

// with static_ptr<>
void func(static_ptr<B> ref);

int main()
{
    static_ptr_container<B> b;
    func(b); // here, func() could inline operator->() from static_ptr<> and
             // static_ptr_container<> and be dead-sure it's dealing with an object
             // `B`; in cases func() is really *only* meant for `B`, static_ptr<>
             // serves both as a compile-time restriction for that type (great!)
             // AND as a big runtime optimization if func() uses `B`'s
             // virtual methods a lot -- and even gets to explore inlining
             // when possible

    return 0;
}

Было бы целесообразно реализовать это?(и не говорите, что это микрооптимизация, потому что это может быть огромная оптимизация ..)

- edit

Я только что заметил, что проблема с static_ptr<> не имеет ничегоделать с проблемой, которую я выставил.Тип указателя сохраняется, но он все еще не встроен.Я думаю, что GCC просто не идет так глубоко, как нужно, чтобы выяснить, что static_ptr_container <> :: value не является ни ссылкой, ни указателем.Извини за это.Но вопрос все еще остается без ответа.

- edit

Я разработал версию static_ptr<>, которая действительно работает.Я немного изменил имя, а также:

template <typename T>
struct static_type_container
{
    // uncomment this constructor if you can't use C++0x
    template <typename ... CtorArgs>
    static_type_container(CtorArgs ... args)
            : value(std::forward<CtorArgs>(args)...) {}

    T value; // yes, it's that stupid.
};

struct A
{
    virtual ~A() {}
    virtual int go() = 0;
};

struct B : public A { int go() { return 1; } };

inline int func(static_type_container<Derived> *ptr)
{
    return ptr->value.go(); // B::go() gets inlined here, since
                            // static_type_container<Derived>::value
                            // is known to be always of type Derived
}

int main()
{
    static_type_container<Derived> d;
    return func(&d); // func() also gets inlined, resulting in main()
                     // that simply returns 1, as if it was a constant
}

Единственный недостаток в том, что пользователь должен получить доступ к ptr->value, чтобы получить реальный объект.Перегрузка operator ->() не работает в GCC.Любой метод, возвращающий ссылку на реальный объект, если он встроенный, нарушает оптимизацию.Как жаль ..

1 Ответ

2 голосов
/ 08 декабря 2010

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

Commenter Хулио Герра указал на идиому C ++ (они называютэто «парадигма» в документах, но я думаю, что это слишком много) под названием Статическое C ++ объектно-ориентированное программирование (SCOOP).Я опубликую это, чтобы дать SCOOP больше видимости.

SCOOP был изобретен для того, чтобы позволить программистам C ++ получить лучшее из мира OOP и GP, сделав так, чтобы оба хорошо играли вместе в C ++.Он нацелен прежде всего на научное программирование из-за увеличения производительности, которое он может принести, и потому, что он может быть использован для повышения выразительности кода.

SCOOP заставляет универсальные типы C ++ эмулировать, казалось бы, все аспекты традиционного объектно-ориентированного программирования - статически.Это означает, что методы шаблона получают функцию, например, возможность надлежащим образом перегружаться и (по-видимому) выдавать гораздо более правильные сообщения об ошибках, чем те, которые обычно вызываются вашей случайной функцией шаблона.

Это также можно использовать для выполнениянекоторые забавные трюки, такие как условное наследование.

То, что я пытался выполнить с помощью static_ptr<>, было именно типом статической объектной ориентации.SCOOP перемещает это на совершенно новый уровень.

Для тех, кто интересуется, я нашел две статьи, говорящие об этом: Статическое C ++ объектно-ориентированное программирование (SCOOP). Преимущества смешивания парадигмы традиционного ООП и общегоПрограммирование и Обобщенная семантика: продолжение статической парадигмы объектно-ориентированного программирования C ++ (SCOOP 2) .

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

Я уверен, что в некоторых обстоятельствах это все же полезно, не говоря уже о очень весело .

Счастливого взлома шаблонов.

...