我手头有大量的模板代码,其中应该用作用户代码模板参数的类具有不同的构造函数签名。我的问题是,我还没有找到在用户代码中调用模板化类的构造函数的好方法。一个最小的工作示例可能如下所示:
#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(),因为编译器将同时检查两个签名,而一个签名将是错误的。我不能对“ if constexpr(){}”使用c ++ 17。
我可以将模板参数“ ShortConstructorInLibrary”传递给我的类,并完美地调用它的构造函数,但是当我使用另一个类时,它当然会因错误的构造函数签名而失败。到目前为止,我使用了一个丑陋的把戏,以至于我实现了两个辅助方法,在该方法中我传递了一个指针,然后让这两个方法实现了构造函数调用,但这对我来说似乎很丑陋。我还摆弄了std :: enable_if <>,但走得并不远。 @Mohit提议使用部分模板专门化,但是在现实世界的代码中,Short ConstructorInLibrary类本身是使用两个... templateed模板参数进行模板化的。给您一个想法:
‘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)
答案 0 :(得分:1)
尝试使用部分专业化方法来实现这一目标,我使用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
}
答案 1 :(得分:1)
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
是此非类型模板参数的默认值,因此您不必无需自己指定。