首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >应用于模板类的CRTP

应用于模板类的CRTP
EN

Stack Overflow用户
提问于 2018-11-15 14:59:59
回答 1查看 93关注 0票数 1

让我们考虑一个CRTP模板类打印,它用于打印派生类:

代码语言:javascript
复制
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;
};

因为我想将基于派生类的打印专门化,就像我们可以通过覆盖来完成这一任务一样,所以我还没有实现该方法。

我们可以包装Integer并这样做,例如:

代码语言:javascript
复制
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;
}

到目前为止,我们假设我要打印包装器的泛型版本:

代码语言:javascript
复制
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>>;
};

如果我专门使用包装器的专门化的打印方法,它会编译并工作:

代码语言:javascript
复制
template <>
auto Print<Wrapper<int>>::print() const -> void
{
  cout << self().m_value << endl;
}

但是,如果我想说“对于包装器的所有专门化,请这样做”,它是行不通的:

代码语言:javascript
复制
template <typename T>
auto Print<Wrapper<T>>::print() const -> void
{
  cout << self().m_value << endl;
}

如果我在以下主要函数上运行该函数:

代码语言:javascript
复制
auto main(int, char**) -> int {
    auto i = Integer{5};
    i.print();

    auto wrapper = Wrapper<int>{5};
    wrapper.print();

    return 0;
}

编译器打印:

代码语言:javascript
复制
50:42: error: invalid use of incomplete type 'struct Print<Wrapper<T> >'
6:8: error: declaration of 'struct Print<Wrapper<T> >'

为什么?我怎么能这么做?这是可能的,还是我必须使我的CRTP类完全专业化?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-11-15 15:15:35

只要你小心,你就可以以一种迂回的方式做这件事。

现场演示

您的Print类将依赖另一个类PrintImpl进行打印。

代码语言:javascript
复制
#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");
    }
};

您将为您的PrintImpl类部分地专门化这个Wrapper

代码语言:javascript
复制
template<class T>
struct PrintImpl<Wrapper<T>>
{
    void operator()(const Wrapper<T>& _val) const
    {
       std::cout << _val.m_value;
    }
};

并确保Wrapper将此PrintImpl声明为friend

代码语言:javascript
复制
friend struct PrintImpl<Wrapper<T>>;

Print类创建一个PrintImpl实例并调用operator()

代码语言:javascript
复制
void print() const
{
    PrintImpl<T>{}(self());
}

只要您的专门化在实际实例化Print类实例之前声明,这就可以工作。

您还可以为您的PrintImpl<T>::operator()类完全专门化,而无需编写类专门化:

代码语言:javascript
复制
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;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53322234

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档