构造函数,模板和非类型参数

时间:2016-02-19 23:41:05

标签: c++ templates constructor traits software-design

我必须依靠int模板参数的某些原因依赖一个类 出于同样的原因,该参数不能成为类的参数列表的一部分,而是它的构造函数的参数列表的一部分(当然,模板化)。

这里出现了问题 也许我错过了一些东西,但我找不到一种简单的方法来为构造函数提供这样的参数,因为它不能推导出来,也不能明确指定。

到目前为止,我发现了以下替代方案:

  • 将上述参数放入类

  • 的参数列表中
  • 创建工厂方法或工厂函数,可以作为factory<42>(params)

  • 的示例调用
  • 为构造函数提供 traits 结构

我试图为最后提到的解决方案创建一个(不那么)最小的工作示例,也是为了更好地解释问题。
示例中的类不是自身的模板类,因为关键点是构造函数,无论如何真正的模板类都是模板类。

#include<iostream>
#include<array>

template<int N>
struct traits {
    static constexpr int size = N;
};

class C final {
    struct B {
        virtual ~B() = default;
        virtual void foo() = 0;
    };

    template<int N>
    struct D: public B{
        void foo() {
            using namespace std;
            cout << N << endl;
        }

        std::array<int, N> arr;
    };

 public:
     template<typename T>
     explicit C(T) {
         b = new D<T::size>{};
     }

     ~C() { delete b; }

     void foo() { b->foo(); }

 private:
     B *b;
};

int main() {
    C c{traits<3>{}};
    c.foo();
}

说实话,上面提到的解决方案都不合适:

  • 将参数移动到类的参数列表中会完全破坏其设计并且不是一个可行的解决方案

  • 工厂方法是我想避免的,但它可以解决问题

  • 到目前为止,traits struct似乎是最好的解决方案,但不知怎的,我并不完全满意

问题很简单:有没有我错过的东西,可能是一个更简单,更优雅的解决方案,我完全忘记的语言细节,或者上面提到的三种方法我必须选择哪种方法?登记/> 任何建议都将不胜感激。

3 个答案:

答案 0 :(得分:7)

您必须传递可以推断的某些。最简单的方法是使用int std::integral_constant的空包装器。由于您只想要int我相信,我们可以将其别名,然后只接受该特定类型:

template <int N>
using int_ = std::integral_constant<int, N>;

您的C构造函数只接受:

 template <int N>
 explicit C(int_<N> ) {
     b = new D<N>{};
 }

 C c{int_<3>{}};

您甚至可以全力以赴为此创建一个用户定义的文字(la Boost.Hana),以便您可以写:

auto c = 3_c; // does the above

另外,考虑将特征转发到D。如果所有地方都是一种类型,元编程会更好地工作。也就是说,仍然接受int_中的C

template <class T>
explicit C(T ) {
    b = new D<T>{};
}

现在D预计会有::value

template <class T>
struct D: public B{
    static constexpr int N = T::value;

    void foo() {
        using namespace std;
        cout << N << endl;
    }

    std::array<int, N> arr;
};

C的角度来看,这两种方式都是一样的,但值得深思。

答案 1 :(得分:2)

我认为对于大多数情况来说,具有“特征”的解决方案是最好的。

只是为了在这个问题上多做一点“混乱”,我将提供两个选择。也许在某些非常具体的情况下 - 它们可以在某种程度上更好。

  1. 模板全局变量 - 您可以将其命名为原型解决方案
  2. C的构造函数与原始代码的区别仅在于:

    class C final {
        // All B and D defined as in OP code
     public:
        // Here the change - c-tor just accepts D<int> 
        template <int size>
        explicit C(D<size>* b) : b(b) {}
    
        // all the rest as in OP code
    };
    

    原型 - 模板全局变量:

    template <int N>
    C c{new C::D<N>()}; 
    // this variable should be rather const - but foo() is not const 
    // and copy semantic is not implemented...
    

    用法:

    int main() {
        // you did not implement copy semantic properly - so just reference taken
        C& c = ::c<3>; 
        c.foo();
    }
    
    1. 使用基类的解决方案 - 并根据int派生类
    2. 这个解决方案虽然看起来非常有希望,但我个人会避免 - 这只会使设计复杂化 - 而且也存在一些对象切片的可能性。

      class CBase {
          // all here as in OP code for C class
      public:
          // only difference is this constructor:
          template<int size>
          explicit CBase(D<size>* b) : b(b) {}
      };
      

      然后 - 最后一堂课:

      template <int N>
      class C final : private CBase {
      public:
          C() : CBase(new CBase::D<N>()) {}
          using CBase::foo;
      };
      

      用法:

      int main() {
          C<3> c;
          c.foo();
      }
      

      问:有人可以问哪种方式使用基类的解决方案比仅添加int作为另一个参数更好。
      基本实现类 A:您不需要拥有相同代码的许多“副本” - 您可以避免模板代码膨胀......

答案 2 :(得分:-2)

使用模板专门化和继承:

#include <iostream>
using namespace std;

template <int num> struct A {
    A() { cout << "generic" << endl; }
};

template <> struct A<1> {
    A() { cout << "implementation of 1" << endl; }
};

template <int num>
struct B : public A<num> {
    B() : A<num>() {}
};

int main(int argc, char *argv[])
{
    B<1> b;
    B<3> b1;
    B<4> b2;
}

编辑,或者您可以更轻松地完成这项工作:

template <int num>
struct A {
    A();
};

template <int num>
A<num>::A() { cout << "general " << num << endl; }

template <>
A<1>::A() { cout << "specialization for 1" << endl; }