Пожалуйста, рассмотрите следующий код:
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.Любой метод, возвращающий ссылку на реальный объект, если он встроенный, нарушает оптимизацию.Как жаль ..