【问题标题】:Initialize nested template classes初始化嵌套模板类
【发布时间】:2016-08-08 14:04:48
【问题描述】:

我有许多模板类,它们以任意顺序一起工作(共享相同的概念)。

假设我有:

template<typename T>
class A {
  T t_;
public:
  void call() {
    // Do something.
    t_.call();
  }
};

template<typename T, typename U>
class B {
  T t_;
  U u_;
public:
  void call() {
    // Do something.
    t_.call();
    // Do something.
    u_.call();
  }
};

class C {
public:
  void call() {
    // Do something.
  }
};

我有以下实例化类:

using Foo = A<B<A<C>,C>>;

让我们假设,C 可能需要一个特殊的构造函数(或初始化函数)才能工作。我只在运行时才知道。

struct C {
  void init(int);
  void call();
};

我怎样才能初始化 Foo 大概?还是任何其他嵌套类组合?

我目前的解决方法是将 C 定义为:

template<typename I>
struct C {
  C() : var_(I::get())
  void call();
};

并在函数内部创建 Foo:

int main()
{
  int i = 0;

  struct HelperC1 {
    static int get(bool set = false, int value = 0) {
      static int value_ = value;
      if (set) value_ = value;
      return value_;
    }
  } helperC1;

  struct HelperC2 {
    static int get(bool set = false, int value = 0) {
      static int value_ = value;
      if (set) value_ = value;
      return value_;
    }
  } helperC2;

  helperC1.get(true, i);
  helperC2.get(true, i+1);

  A<B<A<C<HelperC1>>,C<HelperC2>>> foo;   
  foo.call();

  return 0;
}

Live example.

你看,这个解决方法不是很方便。另一种方法是使用参数调用 Foo 的第一个构造函数并将它们重定向到 C,但这对于不同的类组合非常糟糕,例如:

using Bar = A<B<A<C>,<B<B<A,C>,C>>>;

问题:如何使用运行时参数初始化嵌套(模板)类(更好/以更好、更简洁的方式)?

【问题讨论】:

  • “可能”是指“适当地”吗?
  • 为什么不为你的类 AB(可能还有 getter)提供构造函数。

标签: c++ templates c++11 constructor


【解决方案1】:

您可以使用指针并使用已构造和初始化的对象构建foo。即:

未经测试的代码

template<typename T>
 class A {
  T* t_;

public:
  A(T* valptr) : t_(valptr){}
  ~A(){ delete t_ ; }

  void call() {
    // Do something.
    t_.call();
  }
};

template<typename T, typename U>
class B {
  T* t_;
  U* u_;

public:
  B(T* val1ptr, U* val2ptr):t_(val1ptr), u_(val2ptr){}
  ~B(){delete val1ptr; delete val2ptr;}

  void call() {
    // Do something.
    t_->call();
    // Do something.
    u_->call();
  }
};

class C {
  private:
    int x_;
  public:
  C(int x):x_(x){}
  void call() {
    // Do something.
  }
};

用法:

A<B<A<C>,C>> foo( new B<A<C>,C>(new A<C>(new C(3) ), new C(3) ) );

【讨论】:

  • 为什么是指针?没有它们,您可以获得相同的...进一步您的类不再是 RAII...如果您创建的副本超出范围,则成员指针被删除并且类被破坏。最好使用智能指针。
  • @davidhigh 您能否展示如何在没有指针(或引用)的情况下获得相同的行为。一个虚拟的公共父级将允许摆脱模板,但不能摆脱指针。对于 RAII,应该实现适当的复制构造函数和赋值运算符。根据用例,隐式共享成员类可能会引入其他错误。
【解决方案2】:

此示例使用移动构造和转发辅助函数为分支模板类型构建值。

转发助手包装构造函数,使用类型推断来避免需要为每个构造函数指定复杂的参数。

在此示例中,call() 函数转储对象子对象的参数结构和值。

#include <iostream>
#include <utility>

template<typename T>
class A {
    T t;
public:
    A(T&& t) : t{std::move(t)} {}
    void call() {
        std::cout << "A<";
        t.call();
        std::cout << ">";
    }
};
template <typename T>
inline A<T> mkA(T&& t) { return A<T>{std::forward<T>(t)}; }

template<typename T, typename U>
class B {
    T t;
    U u;
public:
    B(T&& t, U&& u) : t{std::move(t)}, u{std::move(u)} {}

    void call() {
        std::cout << "B<";
        t.call();
        std::cout << ",";
        u.call();
        std::cout << ">";
    }
};
template <typename T, typename U>
inline B<T,U> mkB(T&& t, U&& u) { 
    return B<T,U>{std::forward<T>(t), std::forward<U>(u)};
}

class C {
    int c;
public:
    C(int c) : c{c} {}
    void call() {
        std::cout << "C(" << c << ")";
    }
};

int main() {
    auto bar = mkA(mkB(mkA(C{1}), mkB(mkB(mkA(C{2}),C{3}), C{4})));
    bar.call();
    std::cout << '\n';
}

这个输出:

A<B<A<C(1)>,B<B<A<C(2)>,C(3)>,C(4)>>>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-12
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多