继承(或成员)特征成语
Inheriting (or member) traits idiom
像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 > {
...
};
(为了论证起见,我们忽略一个事实,即必须在namespace std
中声明实际的std::iterator_traits
专业化。我正在尝试对用户代码中可能发生的事情进行熟悉的插图。
这更干净,因为不需要违反成语来处理任何需要花哨步法的特殊情况。主要特征模板不会产生缺少的客户端类不适合某些内容的内部错误,而是根本不需要任何主要特征模板。
从概念上讲,最好将类的质量与其服务的实现分开,无论这种分离是否必要。但是,这种风格确实需要将每个客户端类分成两部分,包括明确的专业化,这有点丑陋。
有人熟悉这个设计空间吗?我倾向于第二个成语,尽管它在实践中看起来很不寻常。但是,那些以前在这里走过的人可能知道一些来龙去脉。
用户定义的特征作为库类型的专用化的问题在于库类型属于库。定义显式专用化需要打开库命名空间,这很丑陋。
备选方案 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 >();
}
};
};
http://ideone.com/QztQ6i
请注意,只要类是可复制和可破坏的,就可以简单地traits_type_entry
t const &
参数t
。
此外,您可以通过让主模板返回从t
派生的类型并删除其构造函数而不是t
本身来防止声明(非自定义)特征类型的对象。
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- 特征::矩阵<双精度,1,3> 结构类型函数中的返回类型函数
- 有没有一种方法可以通过"typedef"为重新定义的基本类型定义特征和强制转换运算符
- 特征命名访问向量段
- 将特征矩阵的向量设置为0
- 特征:模板函数中矩阵的平面图
- basic_string的前导/尾部不区分空格的特征
- 特征 3 类的模板专用化
- 特征 c++:复矩阵的面积双曲正切(atanh)
- C++ 中的特征向量计算
- 根据C++标准的定义实现"is_similar"类型特征
- C++类型特征,以查看是否可以<uint32_t>对类型"K"的任何变量调用"static_cast(k)"
- 有没有办法找到特征矩阵系数的中值?
- 如何将高维数据映射到特征类型?
- 将平面阵列重塑为复杂的特征类型
- 特征 LLT 模块给出不正确的结果?
- 特征模板化函数和维度
- 特征稀疏向量:求最大系数
- 特征 3.3.x:如何在所有行中操作 lamba?
- 继承(或成员)特征成语