应用于模板的 CRTP class
CRTP applied on a template class
让我们考虑一个 CRTP 模板 class Print,它旨在打印派生的 class:
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;
};
因为我想根据派生的 class 专门化打印,就像我们可以通过覆盖来做到这一点一样,我还没有实现该方法。
我们可以包装一个整数,例如:
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;
}
到目前为止一切正常,现在假设我想打印包装的通用版本:
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>>;
};
如果我使用 Wrapper 的专门化来专门化我的 print 方法,它会编译并运行:
template <>
auto Print<Wrapper<int>>::print() const -> void
{
cout << self().m_value << endl;
}
但是如果我想说"for all specializations of Wrapper, do that",那是行不通的:
template <typename T>
auto Print<Wrapper<T>>::print() const -> void
{
cout << self().m_value << endl;
}
如果我运行这个主要功能如下:
auto main(int, char**) -> int {
auto i = Integer{5};
i.print();
auto wrapper = Wrapper<int>{5};
wrapper.print();
return 0;
}
编译器打印:
50:42: error: invalid use of incomplete type 'struct Print<Wrapper<T> >'
6:8: error: declaration of 'struct Print<Wrapper<T> >'
为什么?我怎样才能做到这一点 ?甚至有可能还是我必须对我的 CRTP class 进行完全专业化?
只要你小心一点,你就可以用一些迂回的方式来做到这一点。
Live Demo
您的 Print
class 将依赖另一个 class 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");
}
};
您将针对您的 Wrapper
class:
部分专门化此 PrintImpl
template<class T>
struct PrintImpl<Wrapper<T>>
{
void operator()(const Wrapper<T>& _val) const
{
std::cout << _val.m_value;
}
};
并确保 Wrapper
将此 PrintImpl
声明为 friend
:
friend struct PrintImpl<Wrapper<T>>;
Print
class 创建 PrintImpl
的实例并调用 operator()
:
void print() const
{
PrintImpl<T>{}(self());
}
只要在实际实例化 Print
class.
的实例之前声明您的专业化,此方法就有效
您还可以为您的 Integer
class 完全特化 PrintImpl<T>::operator()
而无需编写 class 特化:
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;
}
让我们考虑一个 CRTP 模板 class Print,它旨在打印派生的 class:
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;
};
因为我想根据派生的 class 专门化打印,就像我们可以通过覆盖来做到这一点一样,我还没有实现该方法。
我们可以包装一个整数,例如:
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;
}
到目前为止一切正常,现在假设我想打印包装的通用版本:
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>>;
};
如果我使用 Wrapper 的专门化来专门化我的 print 方法,它会编译并运行:
template <>
auto Print<Wrapper<int>>::print() const -> void
{
cout << self().m_value << endl;
}
但是如果我想说"for all specializations of Wrapper, do that",那是行不通的:
template <typename T>
auto Print<Wrapper<T>>::print() const -> void
{
cout << self().m_value << endl;
}
如果我运行这个主要功能如下:
auto main(int, char**) -> int {
auto i = Integer{5};
i.print();
auto wrapper = Wrapper<int>{5};
wrapper.print();
return 0;
}
编译器打印:
50:42: error: invalid use of incomplete type 'struct Print<Wrapper<T> >'
6:8: error: declaration of 'struct Print<Wrapper<T> >'
为什么?我怎样才能做到这一点 ?甚至有可能还是我必须对我的 CRTP class 进行完全专业化?
只要你小心一点,你就可以用一些迂回的方式来做到这一点。
Live Demo
您的 Print
class 将依赖另一个 class 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");
}
};
您将针对您的 Wrapper
class:
PrintImpl
template<class T>
struct PrintImpl<Wrapper<T>>
{
void operator()(const Wrapper<T>& _val) const
{
std::cout << _val.m_value;
}
};
并确保 Wrapper
将此 PrintImpl
声明为 friend
:
friend struct PrintImpl<Wrapper<T>>;
Print
class 创建 PrintImpl
的实例并调用 operator()
:
void print() const
{
PrintImpl<T>{}(self());
}
只要在实际实例化 Print
class.
您还可以为您的 Integer
class 完全特化 PrintImpl<T>::operator()
而无需编写 class 特化:
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;
}