我正在尝试制作一种自动创建包装对象的包装类:
#include <memory>
#include <type_traits>
template<typename T>
class Foo {
std::unique_ptr<T> _x;
public:
Foo(); // will initialize _x
};
此外,我希望能够向(对于PIMPL 模式)的T
用户隐藏实现细节。对于单翻译单元示例,假设我有Foo<T>
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...
};
这一切都很好。但是,如果我想要求T
从另一个类派生,编译器会抱怨Bar
不完整:
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");
}
尝试使用相同的检查static_cast
同样失败:
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);
}
但是,似乎如果我添加另一个级别的函数模板,我可以做到这一点:
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>())) { }
请注意,尽管结构相似,但即使以下内容仍会导致不完整类型错误:
// 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>())) { }
这里发生了什么?附加功能是否会以某种方式延迟对 static_assert 的检查?是否有更清洁的解决方案,不涉及添加功能,但仍允许template class Foo<Bar>;
在定义之前放置Bar
?