I do not see why static typing causes difficulty with this. The only gymnastics I have seen in C/C++ do accomplish this is forward declarations. These are not needed because of the type system, but rather because C/C++ requires things to be defined before they are used. For example, java does not have this requirement.
Also, I believe that Go has something simmilar to duck typing, and its a static type system.
This sounds similar to duck typing, but it's significantly less powerful because you pass values into functions with a particular declared type, and you can only access methods available to the declared type of the value.
For example:
interface MyInterface { Bar() }
struct Foo {}
func (this Foo) Bar()
func (this Foo) Baz()
func MyFunction(arg MyInterface) {
arg.Bar() // Fine, because MyInterface has this method.
arg.Baz() // Compiler error because MyInterface does not have this method.
}
In this example, you could use reflection or the unsafe package to call arg.Baz() despite the fact that arg isn't guaranteed to have that method, but it takes some work and you're basically breaking the way the language is meant to be used.This isn't a problem in Python or Ruby because they don't have a notion of declared type in function arguments -- the "type" is resolved at run-time by method lookup.
The thing that I find creates the most boilerplate for me with static typing is useless helper classes that only exist to get around the fact that class A owns object of type B, so you don't want to allow an object of type B have a method that requires its parent A. Instead you create a class C owned by A which everything that B needs for that call can move into, and then let B know about C. (It does not much matter whether you make A inherit from C, or make C owned by A. Though I stylistically strongly prefer the second.)
Yes, you can set up mutual recursion between two classes in C++, but this involves some gymnastics and is frowned on.
In a dynamically typed language you'd just pass objects of type A into a method for type B.
Refactoring out a whole new class out of an existing one is not "a minor amount of boiler plate" in my books. But that is what C++ pushes you to do.