Is the use of models with declared types in the front sure?


I'm building an header-only library, and I've resolved some circular dependency issues by doing something similar to what the code shows.

Basically, I create a private template implementation that allows me to use forward-declared types as if they were included and not forward-declared.

Is there anything dangerous about my approach?

Is there any performance loss? (the library's main focus is performance - the real code has explicit inline suggestions)

Bonus question: is there an impact (positive or negative) on compilation time?

// Entity.h
#include "Component.h"
struct Entity { void a() { ... } }

// Component.h
struct Entity; // forward-declaration
class Component
    Entity& entity;
    template<class T = Entity> void doAImpl() { static_cast<T&>(entity).a(); } 

        // void notWorking() { entity.a(); } <- this does not compile
        void doA() { doAImpl(); }

Is there anything dangerous about my approach?

As long as the template instantiation is, in fact, deferred, not much can go wrong. It could maybe declare intent a bit better if you prohibited an incorrect instantiation:

typename std::enable_if< std::is_same< T, Entity >::value
    && sizeof ( T ) /* Ensure that Entity is not incomplete. */ >::type

On second reading of your code, it looks like the non-template doA function will immediately and prematurely instantiate doAImpl, defeating the templating. I don't think the public interface can be a non-template, since it must cause instantiation of whatever Impl ultimately does the work, but only when the function is actually used. Unless there's another layer of templating protecting the user, it's probably better to do away with the private part and do everything in doA.

Is there any performance loss?

Nope. The function is sure to be inlined either way.

Is there an impact (positive or negative) on compilation time?

The minuscule added complexity will certainly not make a difference.

The only reason not to do this is obvious: it's ugly. And quite likely a violation of separation of concerns.

One workaround would be to use a non-member, free function instead.

struct Entity;
void a( Entity & );

    void doA() { a( entity ); }

Another would be to simply treat Entity.h or whatever as a dependency and include it. I think this would be the most popular solution.

If Component is really not dependent on Entity, then maybe doA belongs in a derived class which should have its own new header, which includes both the existing ones.