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

Constructors, templates and non-type parameters

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

问题就出在这里
也许我遗漏了一些东西,但我看不到向构造函数提供这样一个参数的简单方法,因为它既不能推导也不能明确指定。

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

我试图为最后提到的解决方案创建一个(不是那么)最小的工作示例,也是为了更好地解释问题。
例子中的class本身不是模板class,关键是构造函数,反正真正的是模板class.

#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();
}

老实说,上面提到的 none 个解决方案很适合:

问题很简单:有没有我遗漏的东西,也许是更简单、更优雅的解决方案,我完全忘记的语言细节,或者我必须选择上面提到的三种方法?
如有任何建议,我们将不胜感激。

你必须传入可以推断的东西。最简单的使用方法就是对 int 进行空包装:std::integral_constant。因为你只想要 ints 我相信,我们可以给它取别名然后只接受特定类型:

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>{}};

你甚至可以全力以赴为此创建一个用户定义的文字(a la Boost.Hana)这样你就可以写:

auto c = 3_c; // does the above

此外,考虑简单地将特征转发给 D。如果到处都是类型,元编程会更好。即在C:

中仍然接受相同的int_
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 的用户角度来看,这两种方式都是一样的,但值得深思。

我认为 "traits" 的解决方案对大多数情况来说是最好的。

只是为了多做一点"mess"这一期我再提供两个备选方案。也许在某些非常具体的情况下 - 它们可以在某些方面更好。


  1. 模板全局变量 - 您可以将其命名为 原型解决方案:

class 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. 基于 class 的解决方案 - 并根据 int
  2. 导出 class

这个解决方案虽然看起来很有前途,但我个人会避免 - 这只会使设计复杂化 - 并且这里也存在对象切片的一些可能性。

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) {}
};

然后-决赛class:

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();
}

问: 可以问 Base class 的解决方案比只添加 int 作为另一个参数。
A: 通过基础实现 class 你不需要很多 "copies" 相同的代码 - 你可以避免模板代码膨胀...

使用模板专业化和继承:

#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; }