当前位置: 首页 > 知识库问答 >
问题:

C++17将模板类接口引用传递到同级模板类构造函数中

阴宏爽
2023-03-14

请考虑以下示例:

template <typename Consumer>
class ClassA
{
public:
    template<class... Args>
    explicit ClassA(Args &&... args) :
        consumer_(std::forward<Args>(args)...)
    {

    }

    void consume()
    {
        consumer_.consume();
    }

private:
    Consumer consumer_;
};

template <typename Consumer>
class ClassB
{
public:
    template<class... Args>
    explicit ClassB(Args &&... args) :
        consumer_(std::forward<Args>(args)...)
    {

    }

    void consume()
    {
        consumer_.consume();
    }

private:
    Consumer consumer_;
};

class ClassC
{
public:
    explicit ClassC(int val) :
        val_(val)
    {

    }

    void consume()
    {
        std::cout << "ok " << val_ << std::endl;

    }

private:
    int val_;
};


void usage()
{
    ClassA<ClassB<ClassC>> composed_object(3);
    composed_object.consume();
}

它是一种模板桥(或代理?或策略?)模式,我可以在编译时轻松地编写和更改实现。

由于性能原因,我尽量避免使用动态多态性。

那么,问题是:如何允许ClassB调用一些ClassA方法?

我想到的第一件事是将ClassA的引用传递给ClassB。但是有一个模板化的类链,我不想改变类链的用法。

我可以重写ClassB,如下所示:

template <typename Interface, typename Consumer>
class ClassB
{
public:
    template<class... Args>
    explicit ClassB(Interface &interface, Args &&... args) :
        consumer_(std::forward<Args>(args)...),
        interface_(interface)
    {

    }

    void consume()
    {
        consumer_.consume();
    }

private:
    Consumer consumer_;
    Interface &interface_;
};

因此,它现在需要一个额外的父类接口的模板化参数和一个对构造函数中的父级的引用。

但是我不知道如何在ClassA中指定模板化的param接口而不改变链的用法。

在我看来,这是一种模板参数无穷大循环。

共有1个答案

毕泽宇
2023-03-14

您希望将生产者传递给ClassB而不传递它。我们可以通过CRTP实现这一点。

template <typename Consumer>
class ClassA:private Consumer
{
public:
    template<class... Args>
    explicit ClassA(Args &&... args) :
        Consumer(std::forward<Args>(args)...)
    {

    }
    void say_hello() { std::cout << "hello!\n"; }
    void consume()
    {
        Consumer::consume();
    }
};

并类似地更改classB。一切都继续按原样工作。现在您希望classB找到它的生产者。

template <template<class>class Producer, typename Consumer>
class ClassB:private Consumer
{
public:
    template<class... Args>
    explicit ClassB(Args &&... args) :
        Consumer(std::forward<Args>(args)...)
    {

    }

    void consume()
    {
        GetProducer().say_hello();
        Consumer::consume();
    }

private:
   Producer<ClassB>& GetProducer() { return *static_cast<Producer<ClassB>*>(this); }
   Producer<ClassB> const& GetProducer() const { return *static_cast<Producer<ClassB> const*>(this); }
};

这里我们告诉classB谁为它生成,并添加一个获得它们的生产者的私有方法(假设它们的生产者使用相同的继承组合策略)。

void usage()
{
    ClassA<ClassB<ClassA, ClassC>> composed_object(3);
    composed_object.consume();
}

这里,我们介绍classB如何生成它们。

构造的类知道一些事情。它知道它的构造参数,并且它还有一个隐式的this指针。

在这里,我们通过CRTP和继承将生产者标识插入类中,而不必将其作为参数传递。

使用模板别名,您甚至可以“跳过”级别。但是,如果你搞错了,那么UB很快就会产生效果。

template<template<class...>class Z, class...Us>
struct ztemplate {
  template<class...Ts>
  using result=Z<Us..., Ts...>;
};

template<class...> struct empty_t {};

template<class zProducer, class zConsumer, class...Ts>
class ClassA:
  public zConsumer::template result< ztemplate<ClassA, zProducer>, Ts...>
{
public:
    using Consumer = typename zConsumer::template result< ztemplate<ClassA, zProducer>, Ts... >;
    using Producer = typename zProducer::template result< ztemplate<ClassA>, zConsumer, Ts... >;
    template<class... Args>
    explicit ClassA(Args &&... args) :
        Consumer(std::forward<Args>(args)...)
    {

    }
    void say_hello() { std::cout << "hello!\n"; }
    void consume()
    {
        static_assert( std::is_same_v< typename Consumer::Producer, ClassA > );
        Consumer::consume();
    }
};

template <class zProducer, class zConsumer, class...Ts>
class ClassB:
  public zConsumer::template result< ztemplate<ClassB, zProducer>, Ts...>
{
public:
    using Consumer = typename zConsumer::template result< ztemplate<ClassB, zProducer>, Ts... >;
    using Producer = typename zProducer::template result< ztemplate<ClassB>, zConsumer, Ts... >;
    template<class... Args>
    explicit ClassB(Args &&... args) :
        Consumer(std::forward<Args>(args)...)
    {

    }

    void consume()
    {
        static_assert( std::is_same_v< typename Producer::Consumer, ClassB > );
        GetProducer().say_hello();
        Consumer::consume();
    }

private:
   Producer& GetProducer() { return *static_cast<Producer*>(this); }
   Producer const& GetProducer() const { return *static_cast<Producer const*>(this); }
};

template<class...>
class ClassC
{
public:
    explicit ClassC(int val) :
        val_(val)
    {

    }

    void consume()
    {
        std::cout << "ok " << val_ << std::endl;

    }

private:
    int val_;
};

void usage()
{
    ClassA<ztemplate<empty_t>, ztemplate<ClassB>, ztemplate<ClassC>> composed_object(3);
    composed_object.consume();
}

但这可能太过分了。

这里链中的每个类都被“自动”地告知整个链的结构。(静态断言不是通用的,只是debug通过not-UB检查该特定实例中的工作情况)。

为了减少编译时的膨胀,可以更改传入的classc,如下所示:

template<class Z> struct zAlways {
  template<class...>using result = Z;
};

void usage()
{
    ClassA<zAlways<empty_t>, ztemplate<ClassB>, zAlways<ClassC>> composed_object(3);
    composed_object.consume();
}

classc中删除模板 时。

ZTemplate是将模板作为类传递的模式。我们在这里使用它是因为您不能编写一个以第一个参数为模板、一个以第一个参数为模板、一个以第一个参数为模板、一个以...

zAlways遵循模式,即真实模板在foo::template result中。这里我们创建一个ZTemplate,它总是返回一个特定的类。

但我离题了。

 类似资料:
  • 在C++11之前,类模板和函数模板只能含有固定数量的模板参数。C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板。可变参数模板的加入使得C++11的功能变得更加强大,而由此也带来了许多神奇的用法。 可变参数模板 可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号...: template<ty

  • 我有一个类,它的操作类似于智能指针。以下是重要的比特: 现在,我希望能够从构造,而不是相反。复制赋值运算符也是这样:应该合法,但不应该合法。 现在,我想添加一个模板,专门化或其他东西,但我肯定有一个更好的方法来做这件事。请注意,非常量版本必须将常量版本添加为友元,才能访问constructor/assignmnent运算符中的私有成员。

  • 我有一个模板基类,其模板参数类型为bool。此基类的构造函数参数列表取决于模板参数是true还是false。我想从这个类派生另一个模板类,它的模板参数是任意类型的。我需要这个派生类根据该类型的特征调用该基类的正确构造函数。 下面的例子并不包罗万象。无论是否为整数,基类模板bool可以是任何类型trait。此外,传递给派生类的模板参数的类型可以是任何类型。

  • 问题内容: 尝试访问传递给模板的函数时出现错误: 有人可以让我知道我在做什么错吗? 模板文件(struct.tpl): 调用文件: 这是用于生成struct样板代码的程序(以防万一有人想知道为什么我要在模板中这样做)。 问题答案: 自定义函数需要在解析模板之前进行注册,否则解析器将无法分辨标识符是否为有效的函数名。模板被设计为可静态分析的,这是必需的。 您可以先使用创建一个新的未定义模板,并且除了

  • 本文向大家介绍C++函数模板与类模板实例解析,包括了C++函数模板与类模板实例解析的使用技巧和注意事项,需要的朋友参考一下 本文针对C++函数模板与类模板进行了较为详尽的实例解析,有助于帮助读者加深对C++函数模板与类模板的理解。具体内容如下: 泛型编程(Generic Programming)是一种编程范式,通过将类型参数化来实现在同一份代码上操作多种数据类型,泛型是一般化并可重复使用的意思。泛

  • 在G++中如何做到这一点? 声明类模板ss。[确定] 取消静态函数模板f[ok] 如何专门化f?[错误] 这段代码对于VC++是可以的。 在线:https://godbolt.org/z/qgq6bp