Вызов различных сигнатур конструктора шаблонных типов - PullRequest
0 голосов
/ 05 июля 2018

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

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

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

Я не могу использовать простой if (), потому что компилятор проверит обе подписи и одна из них будет неправильной. Я не могу использовать c ++ 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> >’

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

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

любые советы будут оценены!

(Ubuntu 16.04, gcc)

Ответы [ 2 ]

0 голосов
/ 09 марта 2019

Конструктор MyClass должен создать объект типа T. Предположим, что каждый T, переданный в качестве параметра шаблона, имеет свою сигнатуру конструктора, поэтому вам нужно передать аргументы, необходимые для построения T, в класс MyClass и переслать эти аргументы. Это возможно, так как c ++ 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 является значением по умолчанию для этого нетипичного параметра шаблона, поэтому вам не нужно указывать его самостоятельно.

0 голосов
/ 05 июля 2018

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

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
}
...