4

Considering next simple example:

The header:

// a.hpp
#ifndef A_HPP
#define A_HPP
#include <memory>

class A
{
 public:
  A();

  int foo();

 private:
  struct Imp;
  std::auto_ptr< Imp > pimpl;
};

#endif // A_HPP

The implementation :

// a.cpp
#include "a.hpp"

struct A::Imp
{
 int foo()
 {
  // do something and return the result
 }
};

A::A() : pimpl( new Imp )
{}
int A::foo()
{
  return pimpl->foo();
}

The main :

// main.cpp
#include "header.hpp"
int main()
{
  A a;
  return a.foo();
}

The questions are :
Is the method A::Imp::foo going to get inlined into A::foo?
Does it depend on the implementation what is in that method?

PS I am using gcc (4.3.0 if it matters).

EDIT

I guess I didn't explain very well. What I exactly meant is this. If I use the maximum optimization level, is the // do something and return the result going to be placed in the A::foo() or A::Imp::foo()?
Without optimization, I see that this is not done (the pimpl->foo() is still called).

I understand that A::foo() will never get inlined in main(), but that is not what I am asking.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • Use `boost::scoped_ptr`... and the compiler will scream at you for attempting to `delete` an incomplete type. Hint: write a destructor, and the other two. – Matthieu M. Jun 06 '11 at 11:49
  • @Matthieu Why scoped_ptr? I am not sure it would work with pimple, since the Imp is not defined. – BЈовић Jun 06 '11 at 12:07
  • With this use of `auto_ptr` you need to define `A`'s destructor somewhere where `A::Impl` is defined (e.g. after the constructor definition). `std::unique_ptr` would warn against this, too. – Luc Danton Jun 06 '11 at 12:10
  • @Luc Thanks. I didn't know about that. Wouldn't a default destructor be sufficient? – BЈовић Jun 06 '11 at 12:11
  • Depends what you mean by 'default'. As things are your class is broken because the destructor is defined (by the compiler) at a point where `A::Impl` is incomplete. The problem is not what's in the destructor, it's *where* it's defined. With C++0x you could declare the destructor in the class and use `A::~A() = default;` as the definition after the constructor. This would be a user-declared constructor using the default implementation. – Luc Danton Jun 06 '11 at 12:24

2 Answers2

10

All inlining is implementation dependent. If this matters to you, look at the emitted assembler code.

10

Herb Sutter once made a great article about inlining.

The first question to ask is: when can inlining happen ?

In C++:

  • it may happen at the compilation stage
  • it may happen at the link stage (LTO: Link Time Optimization)

Both times, the mechanism is similar: if the compiler/linker knows about the implementation of the method, it may decide to copy/paste the implementation in place of emitting a call. This decision is based on complex heuristics, and I only know they exist, not what they are about.

The critical point is therefore the knows about the implementation bit.

  • for the compiler: it means defined in the same translation unit
  • for the linker: it means defined in one of the translation units being linked OR in a static library it will linked to... afaik it won't be optimized if the method resides in a DLL

So here: yes, the call pimpl->foo() may be inlined within A::foo. It will depend on both the compiler and the compiling options.

For gcc/clang, if A::Impl::foo is small enough, it could be optimized from O1 onward (unless you pass -fno-inline).

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722