C ++中的内部typedef-好样式还是坏样式?


179

我最近发现自己经常做的事情是声明与该类中特定类相关的typedef,即

class Lorem
{
    typedef boost::shared_ptr<Lorem> ptr;
    typedef std::vector<Lorem::ptr>  vector;

//
// ...
//
};

这些类型然后在代码的其他地方使用:

Lorem::vector lorems;
Lorem::ptr    lorem( new Lorem() );

lorems.push_back( lorem );

我喜欢它的原因:

  • 它减少了由类模板引入的噪音,std::vector<Lorem>变为Lorem::vector等。
  • 它用作意图的声明-在上面的示例中,Lorem类旨在通过boost::shared_ptr向量进行引用计数并存储在向量中。
  • 它允许更改实现-即,如果需要更改Lorem以便boost::intrusive_ptr在以后的阶段(通过)对引用计数进行计数,那么这将对代码产生最小的影响。
  • 我认为它看起来“更漂亮”并且可以说更易于阅读。

我不喜欢的原因:

  • 依赖有时会出现问题-例如,如果您想将a嵌入Lorem::vector到另一个类中,但只需要(或想要)转发声明的Lorem(而不是在其头文件中引入依赖),则最终必须使用显式类型(例如boost::shared_ptr<Lorem>而不是Lorem::ptr),这有点不一致。
  • 这可能不是很常见,因此很难理解吗?

我尝试以我的编码风格为目标,因此对它有其他意见是很好的,这样我可以对自己的想法进行一些剖析。

Answers:


153

我认为这是出色的风格,我自己使用。总是最好尽可能地限制名称的范围,而使用类是在C ++中做到这一点的最佳方法。例如,C ++标准库在类中大量使用typedef。


这是一个很好的点,我不知道它在寻找更“漂亮”这是我潜意识里微妙地指出,有限的范围内是一个很好的事情。我不知道,STL在类模板中主要使用它的事实是否会使它的用法有所不同?在“具体”课程中辩解难吗?
Will Baker

1
标准库由模板而不是类组成,但是我认为两者的理由是相同的。

14

它用作意图的声明-在上面的示例中,Lorem类旨在通过boost :: shared_ptr进行引用计数,并存储在向量中。

这正是它并没有这样做。

如果我在代码中看到'Foo :: Ptr',我绝对不知道它是shared_ptr还是Foo *(记住,STL的:: pointer typedef是T *)。Esp。如果它是一个共享指针,那么我根本不提供typedef,但要在代码中明确使用shared_ptr。

实际上,我几乎从未在模板元编程之外使用typedef。

STL一直在做这种事情

具有成员函数和嵌套typedef定义的概念的STL设计是历史的死胡同,现代模板库使用自由函数和特征类(请参阅Boost.Graph),因为它们不从中排除内置类型。对概念进行建模,因为它使适应未根据给定模板库的概念设计的类型更加容易。

不要将STL用作犯同样错误的理由。


我同意您的第一部分,但是您最近的编辑有些短视。这样的嵌套类型简化了特征类的定义,因为它们提供了合理的默认值。考虑一下新std::allocator_traits<Alloc>类……您不必为编写的每个分配器都专门化它,因为它只是直接从中借用类型Alloc
丹尼斯·齐克福斯

@Dennis:在C ++中,便利性应该放在库的/ user /一侧,而不是在其/ author /一侧:用户希望特征的统一接口,只有trait类才能给出该特征,由于上述原因)。但是,即使是作为Alloc作者,专门std::allocator_traits<>为他的新类型添加元素并不会比添加所需的typedef 困难。我还编辑了答案,因为我的完整回复不适合评论。
Marc Mutz-mmutz 2011年

但这在用户方面。作为用户allocator_traits尝试创建一个自定义的分配,我没有与traits类的十五名成员打扰......所有我需要做的是说typedef Blah value_type;并提供相应的成员函数,默认allocator_traits会找出休息。此外,看一下您的Boost.Graph示例。是的,它大量使用了traits类...但是默认实现是graph_traits<G>简单查询G其自身的内部typedef。
2011年

1
甚至03标准库在适当的地方也使用了traits类...该库的原理不是一般地对容器进行操作,而是对迭代器进行操作。因此,它提供了一个iterator_traits类,以便您的通用算法可以轻松查询适当的信息。再次,默认情况是查询迭代器以获取其自身的信息。它的长处和短处是特征和内部typedef几乎不互斥...它们相互支持。
2011年

1
@Dennis:很有iterator_traits必要,因为它T*应该是的模型RandomAccessIterator,但是您不能将所需的typedef放入T*。一旦有了iterator_traits,嵌套的typedef就变得多余了,我希望可以将它们从那里删除。出于相同的原因(无法添加内部typedef),T[N]不对STL Sequence概念建模,并且您需要使用诸如之类的代码std::array<T,N>。Boost.Range显示了如何定义T[N]可以建模的现代Sequence概念,因为它不需要嵌套的typedef或成员函数。
Marc Mutz-mmutz 2011年


8

Typdef绝对是不错的样式。而且您所有的“我喜欢的理由”都是正确的。

关于您遇到的问题。好吧,向前宣告不是圣杯。您可以简单地设计代码以避免多级依赖。

您可以将typedef移到类之外,但是Class :: ptr比ClassPtr好看得多,我不这样做。就我而言,就像命名空间一样,事物在范围内保持联系。

有时候我做了

Trait<Loren>::ptr
Trait<Loren>::collection
Trait<Loren>::map

它可以是所有域类的默认值,并且可以对某些领域进行特殊化。


3

STL一直在执行此类操作-typedef是STL中许多类的接口的一部分。

reference
iterator
size_type
value_type
etc...

所有typedef都是各种STL模板类的接口的一部分。


是的,我怀疑这是我第一次拿起它的地方。看起来这些似乎更容易辩解吗?如果您碰巧想到“元编程”思路,我不禁将类模板中的typedef视为更类似于变量。
Will Baker

3

对此进行另一次投票是个好主意。我在编写必须在时间和空间上都高效的仿真时开始这样做。所有的值类型都有一个Ptr typedef,它最初是一个Boost共享指针。然后,我进行了一些分析,并将其中一些更改为增强型侵入式指针,而不必更改使用这些对象的任何代码。

请注意,这仅在您知道要在何处使用这些类且所有用途具有相同的要求时才有效。例如,我不会在库代码中使用它,因为在编写库时您不知道使用它的上下文。


2

目前,我正在研究代码,该代码大量使用此类typedef。到目前为止,还好。

但是我注意到,经常有迭代的typedef,这些定义被分成几个类,而且您永远都不真正知道要处理的类型。我的任务是总结隐藏在这些typedef后面的一些复杂数据结构的大小-因此我不能依赖现有的接口。与三到六个级别的嵌套名称空间结合使用,则变得令人困惑。

因此在使用它们之前,需要考虑一些要点

  • 还有其他人需要这些typedef吗?该课程是否被其他课程大量使用?
  • 我会缩短用法还是隐藏课程?(在隐藏的情况下,您也可以想到接口。)
  • 其他人在使用代码吗?他们是如何做到的呢?他们会觉得比较容易还是会感到困惑?

1

我建议将这些typedef移到类之外。这样,您就可以删除对共享指针和向量类的直接依赖,并且仅在需要时才可以包括它们。除非您在类实现中使用这些类型,否则我认为它们不应为内部typedef。

您喜欢它的原因仍然可以匹配,因为它们是通过typedef通过类型别名解决的,而不是通过在类中声明它们来解决的。


那会用typedef污染匿名名称空间吗?typedef的问题在于它隐藏了实际的类型,当包含在多个模块中或由多个模块包含时,这会导致冲突,而这些模块很难找到/修复。将它们包含在名称空间或类中是一个好习惯。
Indy9000

3
名称冲突和匿名名称空间污染与将类型名称保留在类内部或外部无关。您可以与您的类发生名称冲突,而不是与typedef发生冲突。因此,为了避免名称污染,请使用名称空间。在名称空间中声明您的类和相关的typedef。
克特林Pitiş

将typedef放在类中的另一个参数是使用模板函数。例如,当函数收到包含未知字符串类型(字符串或符合您自己的符合字符串的变量)的未知容器类型(向量或列表)时。找出容器有效负载类型的唯一方法是使用typedef'value_type',它是容器类定义的一部分。
Marius

0

当仅在类本身内使用typedef时(即声明为私有),我认为这是一个好主意。但是,出于您给出的确切原因,如果需要在类外知道typedef,我就不会使用它。在这种情况下,我建议将它们移出课堂。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.