I'm trying to make a kind of wrapper class which automatically creates a wrapped object:
#include <memory>
#include <type_traits>
template<typename T>
class Foo {
std::unique_ptr<T> _x;
public:
Foo(); // will initialize _x
};
Furthermore, I want the ability to hide the implementation details of T
from users of Foo<T>
(for the PIMPL pattern). For a single-translation-unit example, suppose I have
struct Bar; // to be defined later
extern template class Foo<Bar>;
// or just imagine the code after main() is in a separate translation unit...
int main() {
Foo<Bar> f; // usable even though Bar is incomplete
return 0;
}
// delayed definition of Bar and instantiation of Foo<Bar>:
template<typename T>
Foo<T>::Foo() : _x(std::make_unique<T>()) { }
template class Foo<Bar>;
struct Bar {
// lengthy definition here...
};
This all works fine. However, if I want to require that T
derives from another class, the compiler complains that Bar
is incomplete:
struct Base {};
template<typename T>
Foo<T>::Foo() : _x(std::make_unique<T>()) {
// error: incomplete type 'Bar' used in type trait expression
static_assert(std::is_base_of<Base, T>::value, "T must inherit from Base");
}
An attempt to achieve the same check using static_cast
fails similarly:
template<typename T>
Foo<T>::Foo() : _x(std::make_unique<T>()) {
// error: static_cast from 'Bar *' to 'Base *', which are not related by inheritance, is not allowed
// note: 'Bar' is incomplete
(void)static_cast<Base*>((T*)nullptr);
}
However, it seems if I add another level of function templating, I can make this work:
template<typename Base, typename T>
void RequireIsBaseOf() {
static_assert(std::is_base_of<Base, T>::value, "T must inherit from Base");
}
// seems to work as expected
template<typename T>
Foo<T>::Foo() : _x((RequireIsBaseOf<Base, T>(), std::make_unique<T>())) { }
Note that even the following still causes an incomplete-type error despite the similar structure:
// error: incomplete type 'Bar' used in type trait expression
template<typename T>
Foo<T>::Foo() : _x((std::is_base_of<Base, T>::value, std::make_unique<T>())) { }
What is going on here? Does the additional function somehow delay the checking of the static_assert? Is there a cleaner solution that doesn't involve adding a function, but still allows placing template class Foo<Bar>;
before the definition of Bar
?