Вызов различных сигнатур конструктора шаблонных типов

У меня есть код с большим количеством шаблонов, в котором классы, которые должны использоваться в качестве параметров шаблона для пользовательского кода, имеют разные подписи конструктора. Моя проблема в том, что я не нашел хорошего способа вызвать конструкторы шаблонных классов в своем пользовательском коде. Мимимальный рабочий пример может выглядеть так:

#include <string>
#include <iostream>
#include <memory>

class ShortConstructorInLibrary {
    std::string myName;
    static const int tag = 1;
public:
    ShortConstructorInLibrary(std::string name) :
            myName(name) {
    }
};

class LongConstructorInLibrary {
private:

    int a;
    double b;
public:
    static const int tag = 2;
    LongConstructorInLibrary(int arg1, double arg2) :
            a(arg1), b(arg2) {
    }
};
//above is library code

template<typename T>
class MyClass {
    std::shared_ptr<T> member_p;
    //i want to call right constructor for both cases:
public:
    MyClass() {

        //how do i call the different constructors properly?!
        member_p = std::shared_ptr<T>(new T("test"));
    }

};

int main() {
    MyClass<ShortConstructorInLibrary> obj;     //works
    //MyClass<LongConstructorInLibrary> obj2;   // wrong constructor signature
}

Здесь у меня есть два класса в библиотеке, один с длинной и несвязанной сигнатурой конструктора, другой с короткой. Я хочу иметь возможность использовать их оба в качестве параметров шаблона. В моем пользовательском классе я каким-то образом должен определить, какие аргументы передавать конструкторам в зависимости от переданного типа.

Я не могу использовать простой if(), потому что компилятор проверит обе подписи, и одна из них будет неправильной. Я не могу использовать С++ 17 для «if constexpr () {}».

Я могу передать параметр шаблона «ShortConstructorInLibrary» в свой класс и вызвать его конструктор в полном порядке, но когда я использую другой класс, он, конечно, не сработает из-за неправильной подписи конструктора. До сих пор я использовал уродливый трюк, реализовав два вспомогательных метода, в которых я передаю указатель, а затем позволяю двум методам реализовывать вызовы конструктора, но мне это кажется уродливым. Я также возился с std::enable_if‹>, но далеко не продвинулся. @Mohit предложил использовать частичную специализацию шаблона, но в реальном коде класс Short ConstructorInLibrary сам по себе шаблонен с парой ... аргументов шаблона шаблона. Чтобы дать вам представление:

‘class CFEM_LOP<Dune::PDELab::QkLocalFiniteElementMap<Dune::GridView<Dune::DefaultLeafGridViewTraits<const Dune::YaspGrid<2> > >, double, double, 1ul>, EngwHillenKnapp2014<MedicalDataManager<double, Dune::YaspGrid<2> >, Dune::YaspGrid<2> >, CFEM_L2OP<Dune::PDELab::QkLocalFiniteElementMap<Dune::GridView<Dune::DefaultLeafGridViewTraits<const Dune::YaspGrid<2> > >, double, double, 1ul> >, Dune::YaspGrid<2> >’

Я понял, что попытка специализировать мой пользовательский код будет адским беспорядком.

Как правильно реализовать вызовы конструктора с возможными разными сигнатурами?

любые подсказки будут оценены!

(убунту 16.04, gcc)


person user5925562    schedule 05.07.2018    source источник
comment
Возможно, вы захотите очистить код, который вы разместили, это вдвое больше фрагмента.   -  person lubgr    schedule 05.07.2018
comment
Если вы используете С++ 17, используйте if constexpr и std::is_same   -  person Mohit    schedule 05.07.2018
comment
если бы я только мог. Тогда я мог бы просто добавить это в свой пользовательский код, и компилятор не стал бы проверять обе ветки if и -let-me-do-it- :-) Хотя мне нужен старомодный способ.   -  person user5925562    schedule 05.07.2018


Ответы (2)


Попробуйте метод частичной специализации для достижения этого, и я использовал std::make_shared для создания std::shared_ptrs

class ShortConstructorInLibrary
{
    std::string myName;
    static const int tag = 1;
public:
    ShortConstructorInLibrary(std::string name) :
        myName(name)
    {
    }
};

class LongConstructorInLibrary
{
private:

    int a;
    double b;
public:
    static const int tag = 2;
    LongConstructorInLibrary(int arg1, double arg2) :
        a(arg1), b(arg2)
    {
    }
};
//above is library code

template<typename T>
class MyClass
{
    std::shared_ptr<T> member_p;
    //i want to call right constructor for both cases:
public:
    MyClass()
    {

        //how do i call the different constructors properly?!
        member_p = std::make_shared<T>("test");
    }

};

template<>
MyClass<LongConstructorInLibrary>::MyClass()
{
    member_p = std::make_shared<LongConstructorInLibrary>(0, 0.0); // pass you values.
}

int main()
{
    MyClass<LongConstructorInLibrary> obj;     //works
                                                //MyClass<LongConstructorInLibrary> obj2;   // wrong constructor signature
}
person Mohit    schedule 05.07.2018
comment
Привет. Спасибо за ваш ответ! - person user5925562; 05.07.2018
comment
(...) Проблема с частичной специализацией заключается в том, что в моем реальном коде два класса, используемые в качестве параметров шаблона, сами по себе являются довольно сложными шаблонами. Чтобы дать вам представление: полный тип shortConstructorClass (в реальном мире): 'class CFEM_LOP‹Dune::PDELab::QkLocalFiniteElementMap‹Dune::GridView‹Dune:: ...to long for comment by 270 персонажи. Если я начну указывать частично, это заведет меня очень далеко вглубь темного кроличьего целого. - person user5925562; 05.07.2018
comment
@ user5925562 используйте псевдоним типа или typedef, чтобы сделать его короче. - person Mohit; 05.07.2018

Предполагается, что конструктор MyClass создает объект типа T. Предположим, что каждый T, переданный в качестве параметра шаблона, имеет другую сигнатуру конструктора, поэтому вам нужно передать аргументы, необходимые для построения T, в класс MyClass и перенаправить эти аргументы. Это возможно, поскольку С++ 11 с вариативными шаблонами, т.е.

template <class T>
struct MyClass
{
  std::shared_ptr<T> member_p;

  template <class... Args>
  MyClass(Args&&... args)
    : member_p(std::make_shared<T>(std::forward<Args>(args)...))
  {}
};

int main() {
  MyClass<ShortConstructorInLibrary> obj1("test");
  MyClass<LongConstructorInLibrary> obj2(1, 2.0);
}

Вам просто нужно быть осторожным с переменными аргументами универсального конструктора, так как это также распространяется на конструктор копирования/перемещения. Чтобы не деактивировать их с помощью вашего письменного конструктора, вам нужно добавить немного кода enable_if. Поскольку вы используете Dune, вы можете просто применить общий шаблон:

template <class... Args, Dune::disableCopyMove<MyClass, Args...> = 0>
MyClass(Args&&... args)
  : member_p(std::make_shared<T>(std::forward<Args>(args)...))
{}

disableCopyMove — это оболочка вокруг enable_if, которая приводит к ошибке замены, если вы передаете MyClass const& или MyClass&& конструктору, чтобы конструкторы копирования и перемещения не были скрыты вашим самоопределяемым конструктором. = 0 необходим, поскольку тип, определенный этой оболочкой enable_if, равен int, а = 0 является значением по умолчанию для этого параметра шаблона, не являющегося типом, поэтому вам не нужно указывать его самостоятельно.

person spraetor    schedule 09.03.2019