У меня есть класс, который по некоторым причинам должен зависеть от параметра шаблона 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();
}

Если честно, ни одно из вышеперечисленных решений не подходит:

  • перемещение параметра в список параметров класса полностью нарушает его структуру и не является жизнеспособным решением

  • фабричный метод - это то, чего я бы хотел избежать, но он может решить проблему

  • структура черт кажется лучшим решением, но почему-то я не полностью удовлетворен

Вопрос довольно простой: есть ли что-то, что я упустил, может быть, более простое и элегантное решение, деталь языка, которую я полностью забыл, или три упомянутых выше подхода - те, из которых я должен выбрать?
Любое предложение будет оценено.

17
skypjack 20 Фев 2016 в 02:41

2 ответа

Лучший ответ

Вы должны передать что-то , что можно вывести. Проще всего использовать пустую оболочку для 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>{}};

Вы даже можете сделать все возможное и создать для этого определенный пользователем литерал (а-ля 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, но стоит подумать.

7
Barry 29 Мар 2016 в 12:14

Использовать специализацию шаблона и наследование:

#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; }
-2
Andrei R. 31 Мар 2016 в 05:35