像std::iterator_traits
这样的Catch-all 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
专门化。我试图熟悉用户代码中可能发生的事情。 。)
这是更清洁的,因为不需要违反成语来处理任何特殊情况,首先需要花哨的步法。而不是主要特征模板产生内部错误,缺少客户端类不适合某些东西,根本不需要任何主要特征模板。
从概念上讲,将类的质量与其服务的实现分开是更好的,无论这种分离是否必要。但是,这种风格确实需要将每个客户端类分成两部分,包括一个显式的特化,这有点难看。
有人熟悉这个设计空间吗?我倾向于第二个成语,虽然它在实践中看起来很不寻常。但是那些曾经在这里跋涉过的人可能已经知道了这些内容。
答案 0 :(得分: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< T >::type
为T
。要将给定类型t
更改为特征类t_traits
,请声明(但不定义)函数t_traits traits_type_entry( t const & )
。此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 >();
}
};
};
注意,t const &
traits_type_entry
参数可以只是t
,只要该类是可复制和可破坏的。
此外,您可以通过让主模板返回从t
派生的类型并删除其构造函数而不是t
本身来阻止声明(非自定义)特征类型的对象。