类型别名和不完整类型

4
我可能过分地想要解决一个本应简单的问题。 我在这里提出了这个问题: 获取编译时基类类型 基本上我试图使类管理自己的指针类型。 我正在包装一个C库,其中一些结构体中嵌入了引用计数,而其他结构体则没有。 对于那些没有引用计数的结构体,我想使用shared_ptr。 对于有引用计数的结构体,我想使用intrusive_ptr。 我想避免依赖程序员的智力来确保使用适当的封装器。 最终,我想添加更多依赖于此行为的功能,但我还没有做到。
@Yakk提出了一种有趣的解决方案,使用模板类型别名,我已经尝试实现它。 不幸的是,我陷入了一个位置,似乎无法满足编译器对循环引用的要求。 我遇到了一个“嵌套名称指定符中命名的不完整类型'Test2'”错误,指向“using pointer =”行。 我也得到了一个奇怪的“定义与返回类型声明不同”的错误,但我认为一旦解决第一个错误,这个问题可能会解决。
我在这种错误类型中找到的大多数参考资料都涉及头文件的排序,但即使所有内容都在一个文件中,我也无法弄清楚如何安排顺序以消除这个问题。
有什么想法吗?
#include <iostream>
#include <memory>


template<typename T>
class Pointered: public std::enable_shared_from_this<T>
{
public:
    using pointer=std::shared_ptr<T>;     //<-- incomplete type named error
    using weakPointer = std::weak_ptr<T>;
};


template<typename T>
using Ptr =  typename T:: pointer;

template<typename T>
using WkPtr = typename T:: weakPointer;


class Test2;

class Test:public Pointered<Test>
{
public:
    Ptr<Test2> f();
};


class Test2:public Pointered<Test2>
{
public:
    Ptr<Test> p;
    Test2(Ptr<Test> ptr):p(ptr){}
};


int main(int argc, const char * argv[])
{
    Ptr<Test> p=std::make_shared<Test>();
    Ptr<Test> p3=p;
    p->f();
    std::cout << "Refcount: " << p.use_count() << std::endl;
}


//definition differs from declaration in return type error here
Ptr<Test2> Test::f()
{
    return Ptr<Test2>(new Test2((Ptr<Test>)shared_from_this()));
}

如果您想根据类型内容使用不同种类的指针,您需要在声明指向它的指针之前完成该类型。听起来像是这种设计的固有属性。 - n. m.
1个回答

4

您无法对嵌套类型进行前向声明,因为您需要外围类型的定义,但在您的情况下,循环依赖关系会阻止这种情况。

首先要考虑的是循环依赖关系是否真的是一个好主意。在大多数情况下,循环依赖关系被认为是一种代码异味(表示设计存在问题的指标)。如果您可以消除循环依赖关系,那么一切都会变得更加容易。

另一种不同的选择是将对嵌套类型的依赖关系移动到类型特征中,该特征可以在类型定义之前外部定义:

template <typename T>
struct pointer_traits;

template <>
struct pointer_traits<Test1> {
   typedef std::shared_ptr<Test1> ptr;
   typedef std::shared_ptr<Test1> wptr;
};

通过将依赖关系移出真实类型,您不再具有循环依赖关系(在语法级别上,您仍应重新审视设计)。然后,您可以根据需要添加语法糖:

template <typename T>
using ptr = typename pointer_traits<T>::ptr;

class Test1 {
    ptr<Test2> p2;
};

如果您真的希望类型也被嵌套,您可以使用继承将它们引入范围内。或者更简单的方式是添加适当的typedef:

class Test1 {
    typedef ptr<Test1> ptr_t;
// ...

请注意,这只是一个粗略的近似值。如果您选择这种方法,可以在特质和类型的细节上进行一些工作,通过添加更多的语法糖使其更易懂。例如,您可以为shared_ptr_traitsintrusive_ptr_traits 提供两个特质,并提供一个单行特质来确定要从哪个特质中获取typedefs,从而将特质(每种类型)的定义减少到一行。


Traits!我之前一直忽略了它们作为STL的某种古老部分,但突然间traits对于我变得相关。感谢你的清晰解释。我也从这里获取了有用的信息:http://erdani.com/publications/traits.html我的编译器坚持在sugar模板中使用“typename”关键字,使其变为: template<typename T> using ptr = typename pointer_traits<T>::ptr; - Omegaman
@GB:是的,typename 是必需的,因为它是一个相关名称。 - David Rodríguez - dribeas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接