I don't see why you need 2-phase for virtual functions.
But yes, I don't think anybody claims is a free lunch. It is annoying, verbose, repetitive, but often necessary to keep the cose base complexity under check.
> I don't see why you need 2-phase for virtual functions.
I might be misremembering the "for virtual functions" part, but the inability of C to call Impl's constructor directly in the presence of a derived class can sometimes force you to delay some of the initialization until after Impl is constructed. ("Necessary" might be too strong here, in that you could find some other workaround too.)
> often necessary to keep the cose base complexity under check.
Hmm... I'm not sure I agree. A pimpl-like idiom can be necessary for solving a few very specific problems, which are explained in [1] better than I can here: (a) ABI stability, (b) slow compilation, and (c) exception safety. There are other niche cases I can think of (e.g. "I need fast/atomic swapping like a reference type, but copying like a value type"), and even those might have better solutions, but none of them really has anything to do with code complexity... they're either domain requirements you either have or don't have, like in (a)/(c)/(d), or they're workarounds for slow toolchains, as in (b). But unless you have requirements/constraints like these, I have a hard time recalling any common situations where pimpl would be the best solution, especially if it's for taming complexity.
But yes, I don't think anybody claims is a free lunch. It is annoying, verbose, repetitive, but often necessary to keep the cose base complexity under check.