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.
};

这无法在主模板中记录该接口(interface),但是仍然存在其他机会。

用一个惯用语编写很多重复的代码来定义元容器类似乎毫无意义,这种惯用语甚至不能保证防止在运行时创建这样的滥用。

也许那完全是倒退。除了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特化。我试图对用户代码中可能发生的事情进行熟悉的说明。)

这是比较干净的,因为不需要处理惯用语就可以处理任何需要特殊处理的异常情况。除了主要特征模板不会产生内部错误(缺少的客户端类不适合某些东西)外,根本不需要任何主要特征模板。

从概念上讲,将类的质量与服务的实现区分开是更好的,而不管这种区分是否必要。但是,这种风格确实需要将每个客户端类分为两部分,包括显式的特化,这很丑陋。

有没有人熟悉这个设计空间?我倾向于第二种习惯用法,尽管在实践中看起来不寻常。但是,以前在这里踩踏过的人可能知道一些来龙去脉。

最佳答案

用户定义特征作为库类型的特化所带来的问题是,库类型属于该库。定义显式特化要求打开库 namespace ,这很丑陋。

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

  • 总是允许最佳的关注点分离(通过将类分为特征和实现)
  • 不需要拆分类
  • 无需打开库 namespace

  • 需要基于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< T >::typeT。要将给定类型的t更改为特征类t_traits,请声明(但不定义)函数t_traits traits_type_entry( t const & )。这个t_traits类可以是t的基类,也可以不是。 traits_type工具无关紧要。因为该函数将通过依赖参数的查找找到,所以可以将其声明为 friend 函数,而在命名空间范围内不声明。

    嵌套在类中的用法(只是为了制作一个困难的测试用例)看起来像这样。对于命名空间中的常规用法,只需删除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

    注意,只要类是可复制和可破坏的,t const &traits_type_entry参数就可以简单地称为t

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

    关于c++ - 继承(或成员)特征习语,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19851629/

    10-13 09:32