在自己的指针上模板化一个类?

时间:2015-12-18 19:47:17

标签: c++ templates c++11 circular-dependency

我想在自己的指针上模板化一个类。我该怎么做呢?

template <typename RefType, typename X>
class Foo {
 public:
  RefType ptr;
  X val;
};
typedef Foo<Foo*, int> Type1;
typedef Foo<std::list<Foo>::iterator itr, int> Type2;

基本上,RefType是一种类型,RefType上的operator *将为我提供类Foo的实例。

有关如何在C ++中执行此操作的任何想法?

编辑: - 为了解决我为什么这样做的动机,在下游的客户代码中,我希望能够做到: -

void clientFunc(Type1& x) {
  Type1 partner_x = *(x.partner);
}

事先不知道x.partner的类型。即x.partner可以是指针或迭代器到标准模板类中。

这是一个非常模糊的用例,我可以不用它,但代码看起来会更优雅。

5 个答案:

答案 0 :(得分:2)

使用基类怎么样?

class Base {};

template <typename RefType, typename X>
class Foo : public Base
{
 public:
  RefType ptr;
  X val;
};
typedef Foo<Base*, int> Type1;

int main()
{
    Base* p = new Type1;
    Type1* pt = static_cast<Type1*>(p);
    pt->val = 3;
    pt->ptr = new Type1;
    (static_cast<Type1*>(pt->ptr))->val = 5;

    // delete etc.
    return 0;
}

您可以通过在Base类中引入适当的虚函数来跳过强制转换。

修改

template<typename T>
class Base{
public:
    virtual Base<T>*& get_ptr() = 0;
    virtual T& get_val() = 0;
};

template <typename RefType, typename X>
class Foo : public Base<X>
{
 public:
  RefType ptr;
  X val;

  Base<X>*& get_ptr()
  {
    return ptr;
  }

  X& get_val()
  {
    return val;
  };

};
typedef Foo<Base<int>*, int> Type1;
typedef Base<int> BaseType;

int main()
{
    BaseType* p = new Type1;
    p->get_ptr() = new Type1;
    p->get_val() = 5;

    // delete etc.
    return 0;
}

答案 1 :(得分:1)

当然有可能,但是由于你的类是模板化的,当你在typedef中声明指向它的指针时,你需要选择显式类型:

using Type1 = Foo<Foo<int*, int>*, int>;

你可以像这样使用它:

Foo<int*, int> myFoo;
int val = 42;
myFoo.ptr = &val;
myFoo.val = 64;

Type1 myObj;
myObj.val = 1337;
myObj.ptr = &myFoo;

通过测试:

std::cout << *(*myObj.ptr).ptr << std::endl; // access int* on a Foo*
std::cout << (*myObj.ptr).val << std::endl; // access int on a Foo*
std::cout << myObj.val << std::endl; // access int on self

打印:

  

42

     

64

     

1337

修改

如果您希望始终指向self,那么我将完全避开RefType模板:

template <typename X>
struct Foo {
  Foo* ptr; // ptr to Foo<X>
  X val;
};

并像这样使用它:

Foo<int> first;
first.val = 42;
first.ptr = &first; // now it refers to itself.


std::cout << first.val << std::endl;

// lets go nuts:
std::cout << first.ptr->ptr->ptr->ptr->val << std::endl;

这实际上和以前一样,除非现在指向Foo的实例将始终与其父实例相同。

答案 2 :(得分:1)

你编写的内容不能编译的原因是Foo不能在没有模板参数的情况下站在其声明之外,即使是指针,例如Foo*中的typedef Foo<Foo*, int> Type1;

如果您按照自己的方式声明了类,则可以在该typedef中递归声明Foo<RefType, X>个指针,但是您必须在某个时刻使用非Foo* RefType才能使其工作。如:

typedef Foo<Foo<Foo<int*, int>*, int>*, int> Type1;

这可能不是你想要做的事情。

同样的问题适用于typedef Foo<std::list<Foo>::iterator itr, int> Type2;。你不能只说Foo,你必须说哪个 Foo。这是一个问题,因为模板参数可能会有无限递归。

有很多可能的方法可以解决这个问题。最简单的是修改类声明。这将有效:

template<typename X>
class Foo
{
public:
    Foo* ptr;
    X val;
};
typedef Foo<int> Type1;

如果由于某种原因您无法更改模板,可以使用void*并将其强制转换为拟合Foo*

template<typename RefType, typename X>
class Foo
{
public:
    RefType ptr;
    X val;
};
typedef Foo<void*, int> Type1;

int main( )
{
    Type1 test;
    Type1* test2 = static_cast< Type1* >( test.ptr );
}

然而,这更容易出错。

答案 3 :(得分:1)

使用partial template specialization

template <typename RefType, typename X>
class Foo
{
public:
    RefType ptr;
    X val;
};

template <typename X>
class Foo<void,X>
{
public:
    using TypeSelf = Foo;
    TypeSelf* ptr;
    X val;
};

typedef Foo<void,int> Type1;

Foo<void,int>现在成员ptr的类型为Foo<void,int>*

答案 4 :(得分:1)

转过jenas的答案:

不是创建基类,而是从Foo派生而不是使用typedef。这与CRTP类似。

struct Type1 : public Foo<Type1 *, int> { };
struct Type2 : public Foo<std::list<Type2>::iterator, int> { };

您可以像使用typedef一样使用Type1和Type2。