请考虑以下代码:
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
}
有什么想法吗?那么其他编译器呢?这种优化常见吗? (我对这种编译器的洞察力很新,所以我很好奇)
如果第二种情况有效,我可以创建一些非常棒的模板,例如:
template <typename T>
class static_ptr_container
{
public:
typedef T st_ptr_value_type;
operator T *() { return &value; }
operator const T *() const { return &value; }
T *operator ->() { return &value; }
const T *operator ->() const { return &value; }
T *get() { return &value; }
const T *get() const { return &value; }
private:
T value;
};
template <typename T>
class static_ptr
{
public:
typedef static_ptr_container<T> container_type;
typedef T st_ptr_value_type;
static_ptr() : container(NULL) {}
static_ptr(container_type *c) : container(c) {}
inline operator st_ptr_value_type *() { return container->get(); }
inline st_ptr_value_type *operator ->() { return container->get(); }
private:
container_type *container;
};
template <typename T>
class static_ptr<static_ptr_container<T>>
{
public:
typedef static_ptr_container<T> container_type;
typedef typename container_type::st_ptr_value_type st_ptr_value_type;
static_ptr() : container(NULL) {}
static_ptr(container_type *c) : container(c) {}
inline operator st_ptr_value_type *() { return container->get(); }
inline st_ptr_value_type *operator ->() { return container->get(); }
private:
container_type *container;
};
template <typename T>
class static_ptr<const T>
{
public:
typedef const static_ptr_container<T> container_type;
typedef const T st_ptr_value_type;
static_ptr() : container(NULL) {}
static_ptr(container_type *c) : container(c) {}
inline operator st_ptr_value_type *() { return container->get(); }
inline st_ptr_value_type *operator ->() { return container->get(); }
private:
container_type *container;
};
template <typename T>
class static_ptr<const static_ptr_container<T>>
{
public:
typedef const static_ptr_container<T> container_type;
typedef typename container_type::st_ptr_value_type st_ptr_value_type;
static_ptr() : container(NULL) {}
static_ptr(container_type *c) : container(c) {}
inline operator st_ptr_value_type *() { return container->get(); }
inline st_ptr_value_type *operator ->() { return container->get(); }
private:
container_type *container;
};
在许多情况下,可以使用这些模板来避免虚拟调度:
// 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;
}
实施它是否可行? (并且不要继续说这是微观优化,因为它可能是一个巨大的优化......)
- 编辑
我刚注意到static_ptr<>
的问题与我暴露的问题无关。保留指针类型,但它仍然不是内联的。我猜GCC只是不需要找到static_ptr_container&lt;&gt; :: value不是引用也不是指针。对于那个很抱歉。但问题仍然没有答案。
- 编辑
我已经找到了实际有效的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中不起作用。任何返回对实际对象的引用的方法,如果它是内联的,都会破坏优化。太可惜了..
答案 0 :(得分:2)
这不是一个明确的答案,但我想我可能会发布它,因为它可能对某些人有用。
评论者Julio Guerra指出了一个C ++习语(他们在论文中将其称为“范式”,但我认为有点太多)称为 Static C ++面向对象编程(舀)。我将发布此内容,以便为SCOOP提供更多可见性。
SCOOP的发明是为了让C ++程序员通过在C ++中很好地协同工作来充分利用OOP和GP世界。它主要针对科学编程,因为它可以带来性能提升,因为它可以用来提高代码表现力。
SCOOP使得C ++泛型类似于静态地模拟传统的面向对象编程的所有方面。这意味着模板方法具有特征,例如,能够正确地重载并且(显然)发出比通常由临时模板函数引起的错误消息更恰当的错误消息。
它也可以用来做一些有趣的技巧,比如条件继承。
我试图用static_ptr<>
完成的是一种静态的面向对象。 SCOOP将其提升到一个全新的水平。
对于有兴趣的人,我发现有两篇论文在讨论这个问题:A Static C++ Object-Oriented Programming (SCOOP) Paradigm Mixing Benefits of Traditional OOP and Generic Programming和Semantics-Driven Genericity: A Sequel to the Static C++ Object-Oriented Programming Paradigm (SCOOP 2)。
这个成语并非没有自己的缺点:它是那些不常见的事情之一,应该是你最后的手段,因为人们很可能很难搞清楚你做了什么等等。你的代码也会变得更加冗长你可能会发现自己无法做你认为可能的事情。
我确信它在某些情况下仍然有用,更不用说真正的有趣。
快乐的模板黑客攻击。