It’s not just a question of indirection, but the cost of executing lookup_vtable itself - which, as far as I can tell, would have to occur on each method call to a multi-trait object (albeit with some optimizations possible).
What would the representation of TraitId be? You’d expect it could be similar or identical to the existing type_id, which returns a statically known u64 based on hashing the contents of a type (this is how Any works). But this system has a long-known flaw: 64 bits are not enough to reliably prevent hash collisions.
One proposed solution was to embed the actual trait names and add a strcmp-like operation. An alternative would be to make the IDs much larger, large enough to use a collision-resistant cryptographic hash. I think a better approach would be to use globally-unique symbol names and letting the linker sort out uniqueness (just once, at link or load time when using static and dynamic linking, respectively); but I think there are concerns that that isn’t possible on all platforms. If it is possible, IDs would become pointers. Doing a single comparison would be as cheap as with the current 64-bit hash, but for multiple comparisons like you envision, since the compiler doesn’t know the exact value of the pointer in advance, it wouldn’t be able to (hypothetically) pre-generate a hash table or even a binary search in advance; it would have to use a linear search. Then again, a linear search of, say, <= 4 values (probably the common case for multi-trait objects) may be faster than any more complicated scheme.
Whatever the case, the cost of figuring out the right vtable might become nontrivial, more like, e.g., an Objective-C method call than a C++ virtual call.
This approach is definitely possible, and it might even be the best one, but I wouldn’t be sure before seeing an implementation and benchmarks.
Personally I suspect the best approach might be the “exponential, but low constant factor” design, where a multi-vtable pointer points to an array of single-vtable pointers. But I’m just guessing.
In any case, has anyone tried to prototype any of these approaches in a library? Shouldn’t be that hard…