I have a type called "Watchable", which can be pointed at by a "WatchPtr" (a template which accepts any type extending Watchable). When the Watchable object is destroyed, all WatchPtrs to it are nullified. This works great, but the problem is without a full definition of the type being pointed to, the compiler fails to verify that the type extends Watchable. This makes many places where a raw pointer could (and should) be replaced with a WatchPtr impossible (at least if you don't want to reorganize your code in a totally senseless manner), as a full definition would create an include-cycle. I have two things to say about this:
1) This is so goddamn stupid, it makes me angry. In my opinion forward-declarations are by far the worst part of writing C++: why should I have to write code just so I can write it again somewhere else? If the compiler can find the definition of a function/class among the files I gave it, it should just be able to, regardless of the order they are referenced within those files. Nearly every modern language supports this. I don't want to have to reorganize all my code just so the parser is happy, that completely defeats any ability to write sensible, organized code. If modules are the solution to this, they can't come soon enough.
2) In the mean time, how can I fix this? My current solution is to just forgo any kind of verification, and add a comment noting that the given type MUST extend "Watchable". This is something that should (and can) be verified at compile-time, just not with the way C++ is currently written.
1. Forward declaration enables making a class that contains a pointer to an unspecified type, but I agree that if the type is visible anywhere in the translation unit, the compiler should have no need to complain. Single pass compilation is an optimization from 30+ years ago and has no place today.
2. I'm not sure I understand what the big deal is. Could you post a brief outline of what your code looks like right now?
/** This is a pointer that let's you observe the lifetime of types extending "Watchable" */
template <typename T>
struct WatchPtr final
{
// Some more stuff...
// Make sure that "T" extends "Watchable"
// static_assert(std::is_base_of<Watchable, T>::value, "...");
// *** lazy static_assert: postpone evaluation till the constructor is instantiated ***
WatchPtr( T* p = nullptr ) : value(p)
{ static_assert(std::is_base_of<Watchable, T>::value, "..."); }
private:
T* value;
};
/** This is a pointer that let's you observe the lifetime of types extending "Watchable" */
template <typename T>
struct WatchPtr final
{
// Some more stuff...
// Make sure that "T" extends "Watchable"
// static_assert(std::is_base_of<Watchable, T>::value, "...");
// *** lazy static_assert: postpone evaluation till the constructor is instantiated ***
template <typename T2>
static WatchPtr<T2> create(Watchable &w){
return WatchPtr<T2>(&w);
}
private:
WatchPtr( T* p = nullptr ) : value(p){}
T* value;
};