像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可以组合成两全其美的模式,
需要基于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 函数,而在命名空间范围内不声明。嵌套在类中的用法(只是为了制作一个困难的测试用例)看起来像这样。对于命名空间中的常规用法,只需删除
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/