【问题标题】:Calling different constructor signatures of templated Types调用模板化类型的不同构造函数签名
【发布时间】:2018-07-05 08:51:12
【问题描述】:

我手头有一个模板繁重的代码,其中应该用作用户代码模板参数的类具有不同的构造函数签名。我的问题是,我还没有找到在我的用户代码中调用模板类的构造函数的好方法。一个最小的工作示例可能如下所示:

#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 类本身是用几个 ...templated 模板参数进行模板化的。给你一个想法:

‘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)

【问题讨论】:

  • 你可能想清除你发布的代码,它是sn-p的两倍。
  • 如果您使用的是 c++17,请使用 if constexprstd::is_same
  • 如果我可以的话。然后我可以把它扔到我的用户代码中,编译器不会检查 if 和 -let-me-do-it- 的两个分支 :-) 不过我需要一种老式的方法。

标签: c++ templates constructor signatures


【解决方案1】:

尝试部分专业化方法来实现这一点,我使用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
}

【讨论】:

  • 嗨。谢谢你的回答!
  • (...) 部分特化的问题是,在我的真实世界代码中,用作模板参数的两个类本身就是相当复杂的模板。给你一个想法:shortConstructorClass 的完整类型(在现实世界中)是:'class CFEM_LOP<:pdelab::qklocalfiniteelementmap ...to long for the comment by>
  • @user5925562 使用类型别名或 typedef 使其更短。
【解决方案2】:

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&amp;MyClass&amp;&amp; 传递给构造函数,这样复制和移动构造函数就不会被您自己隐藏定义的构造函数。 = 0 是必须的,因为这个enable_if 包装器定义的类型是int,而= 0 是这个非类型模板参数的默认值,所以你不需要自己指定。

【讨论】:

    猜你喜欢
    • 2011-07-05
    • 1970-01-01
    • 2019-03-14
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-09
    • 1970-01-01
    相关资源
    最近更新 更多