【问题标题】:Inheriting (or member) traits idiom继承(或成员)特质成语
【发布时间】:2013-11-08 04:30:01
【问题描述】:

std::iterator_traits 这样的包罗万象的特征类通过将类型的属性与其定义分开来很有用,例如,可以在定义完成之前使属性可用。

在每个客户端类本身之外定义特征是不方便的,因为特征通常也有一个成员的位置。这就是为什么std::iterator_traits 的通用实现是根据其模板参数的成员来定义的。

template< typename it >
struct iterator_traits {
    typedef typename it::category category;
    typedef typename it::value_type value_type;
    // etc
};

使用继承不是更容易,编译器的工作量也更少吗?

template< typename t >
struct t_traits : public t {
    t_traits() = delete; // Prevent runtime instances.
};

这无法在主模板中记录界面,但无论如何还有其他机会。

编写大量重复代码来定义元容器类似乎毫无意义,甚至不能保证防止诸如在运行时创建这样的滥用。


或者这完全是倒退。除了std::iterator_traits,我们还有std::iterator,这是一个伪抽象基类,其成员大多相同。这种冗余是一种代码味道。如果自定义迭代器看起来像这样不是更好吗?

template<>
struct iterator_traits< struct my_iterator > {
    typedef random_access_iterator_tag category;
    typedef foo value_type;
    ...
};
struct my_iterator : iterator_traits< struct my_iterator > {
    ...
};

(为了争论,让我们忽略一个实际的std::iterator_traits 特化必须在namespace std 中声明的事实。我试图对用户代码中可能发生的事情进行熟悉的说明。)

这更简洁,因为不需要违反成语来处理首先需要花哨的步法的特殊情况。与其让主要特征模板产生缺失的客户端类不适合某些东西的内部错误,不如根本不需要任何主要特征模板。

从概念上讲,将类的质量与其服务的实现分开会更好,无论这种分离是否必要。但是,这种风格确实需要将每个客户端类分成两部分,包括显式特化,这有点难看。


有人熟悉这个设计空间吗?我倾向于第二个成语,尽管它在实践中看起来很不寻常。但是,那些曾经在这里踩过的人可能都知道其中的来龙去脉。

【问题讨论】:

    标签: c++ design-patterns idioms typetraits


    【解决方案1】:

    将用户定义的特征作为库类型的特化的问题在于库类型属于该库。定义显式特化需要打开库命名空间,这很丑。

    备选方案 1 和 2 可以组合成两全其美的模式

    • 始终允许优化关注点分离(通过将类拆分为特征和实现)
    • 不需要拆分一个类
    • 从不需要打开库命名空间

    需要以基于 ADL 的元函数将任何类映射到其特征的形式进行额外的粘合。

    template< typename t >
    t traits_type_entry( t const & ); // Declared, never defined.
    
    template< typename t >
    using traits_type = decltype( traits_type_entry( std::declval< t >() ) );
    

    默认情况下,T 用作自己的特征类型,因为traits_type&lt; T &gt;::typeT。要将给定类型t 的此更改为特征类t_traits,请声明(但不定义)函数t_traits traits_type_entry( t const &amp; )。这个t_traits 类可能是也可能不是t 的基类; traits_type 设施不在乎。因为该函数将通过参数依赖查找找到,所以它可能被声明为在命名空间范围内没有声明的友元函数。

    嵌套在类中的用法(只是为了制作一个困难的测试用例)看起来像这样。对于命名空间中的常规用法,只需删除 friend 关键字即可。

    class outer_scope {
        struct special;
        struct special_traits {
            typedef int value_type;
            constexpr static int limit = 5;
        };
        friend special_traits traits_type_entry( special const & );
    
        struct unspecial {
            typedef double baz_type;
            int table[ util::traits_type< special >::limit ];
        };
    
        struct special : special_traits {
            void f() {
                 std::pair< typename util::traits_type< unspecial >::baz_type,
                            value_type >();
            }
        };
    };
    

    http://ideone.com/QztQ6i

    注意,traits_type_entryt const &amp; 参数可以简单地为 t,只要该类是可复制和可破坏的。

    此外,您可以通过让主模板返回从 t 派生的类型并删除其构造函数而不是 t 本身来防止声明(非自定义)特征类型的对象。

    【讨论】:

      猜你喜欢
      • 2012-02-13
      • 1970-01-01
      • 2019-03-23
      • 2020-09-01
      • 2017-01-05
      • 2018-05-11
      • 2019-06-01
      • 2010-09-05
      • 2015-06-10
      相关资源
      最近更新 更多