[英]CRTP applied on a template class
Let's consider a CRTP template class Print which is meant to print the derived class:让我们考虑一个用于打印派生类的 CRTP 模板类 Print:
template <typename T>
struct Print {
auto print() const -> void;
auto self() const -> T const & {
return static_cast<T const &>(*this);
}
private:
Print() {}
~Print() {}
friend T;
};
Because I want to specialize print based on the derived class like we could do this with an override, I don't implement the method yet.因为我想根据派生类专门进行打印,就像我们可以通过覆盖来做到这一点一样,所以我还没有实现该方法。
We can wrap an Integer and do so for example:我们可以包装一个 Integer 并这样做,例如:
class Integer :
public Print<Integer>
{
public:
Integer(int i) : m_i(i) {}
private:
int m_i;
friend Print<Integer>;
};
template <>
auto Print<Integer>::print() const -> void {
std::cout << self().m_i << std::endl;
}
This works so far, now let's say I want to Print a generic version of a wrapper:到目前为止,这有效,现在假设我想打印包装器的通用版本:
template <typename T>
class Wrapper :
public Print<Wrapper<T>>
{
public:
Wrapper(T value) : m_value(std::move(value)) {}
private:
T m_value;
friend Print<Wrapper<T>>;
};
If I specialize my print method with a specialization of the Wrapper it compile and works:如果我用 Wrapper 的专业化来专业化我的打印方法,它会编译并工作:
template <>
auto Print<Wrapper<int>>::print() const -> void
{
cout << self().m_value << endl;
}
But if I want to say "for all specializations of Wrapper, do that", it doesn't work:但是,如果我想说“对于 Wrapper 的所有专业,都这样做”,则行不通:
template <typename T>
auto Print<Wrapper<T>>::print() const -> void
{
cout << self().m_value << endl;
}
If I run this over the following main function:如果我在以下主要功能上运行它:
auto main(int, char**) -> int {
auto i = Integer{5};
i.print();
auto wrapper = Wrapper<int>{5};
wrapper.print();
return 0;
}
The compiler print:编译器打印:
50:42: error: invalid use of incomplete type 'struct Print<Wrapper<T> >'
6:8: error: declaration of 'struct Print<Wrapper<T> >'
Why ?为什么 ? How can I do that ?
我怎样才能做到这一点 ? Is it even possible or do I have to make a complete specialization of my CRTP class ?
甚至有可能还是我必须对我的 CRTP 课程进行完全专业化?
You can do this in a bit of a roundabout way so long as you're careful.只要你小心,你就可以以一种迂回的方式做到这一点。
Your Print
class will rely on yet another class PrintImpl
to do the printing.您的
Print
类将依赖另一个类PrintImpl
来进行打印。
#include <type_traits>
template<class...>
struct always_false : std::false_type{};
template<class T>
struct PrintImpl
{
void operator()(const T&) const
{
static_assert(always_false<T>::value, "PrintImpl hasn't been specialized for T");
}
};
You'll partially specialize this PrintImpl
for your Wrapper
class:您将为您的
Wrapper
类部分地专门化这个PrintImpl
:
template<class T>
struct PrintImpl<Wrapper<T>>
{
void operator()(const Wrapper<T>& _val) const
{
std::cout << _val.m_value;
}
};
And make sure that Wrapper
declares this PrintImpl
to be a friend
:并确保
Wrapper
将此PrintImpl
声明为friend
:
friend struct PrintImpl<Wrapper<T>>;
The Print
class creates an instance of PrintImpl
and calls operator()
: Print
类创建PrintImpl
的实例并调用operator()
:
void print() const
{
PrintImpl<T>{}(self());
}
This works so long as your specializations are declared before you actually instantiate an instance of the Print
class.只要在您实际实例化
Print
类的实例之前声明您的特化,这就会起作用。
You can also fully specialize PrintImpl<T>::operator()
for your Integer
class without writing a class specialization:您还可以为您的
Integer
类完全特化PrintImpl<T>::operator()
,而无需编写类特化:
class Integer :
public Print<Integer>
{
public:
Integer(int i) : m_i(i) {}
private:
int m_i;
friend PrintImpl<Integer>;
};
template <>
void PrintImpl<Integer>::operator()(const Integer& wrapper) const {
std::cout << wrapper.m_i << std::endl;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.