特质与政策有什么区别? [英] What is the difference between a trait and a policy?

查看:273
本文介绍了特质与政策有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  template< int ModeT,bool IsAsync,bool IsReentrant> ServerTraits; 

稍后我有我的服务器对象本身:

 模板< typename TraitsT> 
class Server {...};

我的问题是我的使用上面是我的命名错误?我的模板参数实际上是一个政策而不是特质?



什么时候模板参数是特质与策略?

解决方案

政策



策略是通过继承来将注入行为的类(或类模板)插入到父类中。通过将父接口分解为正交(独立)维,策略类形成更复杂接口的构建块。经常看到的模式是使用库提供的默认值作为用户定义的模板(或模板模板)参数提供策略。标准库中的示例是分配器,它们是所有STL容器的策略模板参数。

  template< class T,class Allocator = std :: allocator< T>>类矢量

这里,分配器模板参数本身也是一个类模板!)将内存分配和释放策略注入父类 std :: vector 。如果用户不提供分配器,则使用默认的 std :: allocator< T>



As在基于模板的多边形中是典型的,策略类的接口要求是隐式和语义(基于有效表达式),而不是显式和语法(基于虚拟成员函数的定义)。



请注意,最近的无序关联容器有多个策略。除了通常的 Allocator 模板参数之外,他们还会采用 Hash 策略,默认为 std :: hash< Key> 函数对象。这允许无序容器的用户按照多个正交维度(内存分配和散列)来配置它们。



特征



Traits是从通用类型提取属性的类模板。有两种特征:单值特征和多重特征。单值特征的例子是标题< type_traits>

 模板<类T> 
struct is_integral
{
static const bool value / * =如果T是整数,则为true,否则为false * /;
typedef std :: integral_constant< bool,value>类型;
};

单值特征通常用于模板元编程和SFINAE技巧根据类型条件来重载一个函数模板。



多值特征的例子是来自标题< iterator>的iterator_traits和allocator_traits。 < memory> 。由于traits是类模板,它们可以是专门的。以下 t *



的专业化示例 iterator_traits pre> 模板< T>
struct iterator_traits< T *>
{
using difference_type = std :: ptrdiff_t;
使用value_type = T;
使用pointer = T *;
using reference = T& ;;
使用iterator_category = std :: random_access_iterator_tag;
};

表达式 std :: iterator_traits< T> :: value_type 使得即使对于原始指针也可以使用完整的迭代器类的通用代码(因为原始指针没有成员 value_type )。 p>

政策和特质之间的相互作用



在编写自己的通用库时,重要的是考虑用户可以专业化的方式你自己的类模板。然而,必须小心,不要让用户成为一个定义规则的受害者,方法是使用特征来进行注入,而不是提取行为。要将Andrei Alexandrescu这个旧帖子改写为


根本的问题是,看不到专门的
版本的特征的代码仍然可以编译,很可能链接,
有时甚至可能跑。这是因为在没有
明确的专业化的情况下,非专门的模板踢,可能
实现一个通用的行为,适用于您的特殊情况为
好​​。因此,如果一个应用程序中的所有代码都没有看到
相同的特征定义,则ODR将被违反。


C ++ 11 std :: allocator_traits 通过强制所有STL容器只能从他们的分配器政策通过 std :: allocator_traits< Allocator> 。如果用户选择不提供或忘记提供一些必需的策略成员,traits类可以进入并为缺少的成员提供默认值。因为 allocator_traits 本身不能专门,用户总是必须传递一个完全定义的分配器策略,以便自定义他们的容器内存分配,并且不会发生静默的ODR违规。



请注意,作为一个图书馆作家,人们仍然可以专注于traits类模板(如STL在 iterator_traits< T *> ),但是将所有用户定义的专业化通过策略类传递到可以提取专门行为的多值特征(如STL在 allocator_traits< A>



更新:traits类的用户自定义专业化的ODR问题主要发生在traits用作全球类模板,并且您不能保证所有未来的用户都将看到所有其他用户定义的专业化。策略是本地模板参数,并包含所有相关定义,允许用户定义而不会干扰其他代码。只包含类型和常量的本地模板参数 - 但不包括行为功能 - 可能仍然被称为traits,但是对于其他代码,如 std :: iterator_traits std :: allocator_traits


I have a class whose behavior I am trying to configure.

template<int ModeT, bool IsAsync, bool IsReentrant> ServerTraits;

Then later on I have my server object itself:

template<typename TraitsT>
class Server {...};

My question is for my usage above is my naming misnamed? Is my templated parameter actually a policy instead of a trait?

When is a templated argument a trait versus a policy?

解决方案

Policies

Policies are classes (or class templates) to inject behavior into a parent class, typically through inheritance. Through decomposing a parent interface into orthogonal (independent) dimensions, policy classes form the building blocks of more complex interfaces. An often seen pattern is to supply policies as user-definable template (or template-template) parameters with a library-supplied default. An example from the Standard Library are the Allocators, which are policy template parameters of all STL containers

template<class T, class Allocator = std::allocator<T>> class vector;

Here, the Allocator template parameter (which itself is also a class template!) injects the memory allocation and deallocation policy into the parent class std::vector. If the user does not supply an allocator, the default std::allocator<T> is used.

As is typical in template-based polymporphism, the interface requirements on policy classes are implicit and semantic (based on valid expressions) rather than explicit and syntactic (based on the definition of virtual member functions).

Note that the more recent unordered associative containers, have more than one policy. In addition to the usual Allocator template parameter, they also take a Hash policy that defaults to std::hash<Key> function object. This allows users of unordered containers to configure them along multiple orthogonal dimensions (memory allocation and hashing).

Traits

Traits are class templates to extract properties from a generic type. There are two kind of traits: single-valued traits and multiple-valued traits. Examples of single-valued traits are the ones from the header <type_traits>

template< class T >
struct is_integral
{
    static const bool value /* = true if T is integral, false otherwise */;
    typedef std::integral_constant<bool, value> type;
};

Single-valued traits are often used in template-metaprogramming and SFINAE tricks to overload a function template based on a type condition.

Examples of multi-valued traits are the iterator_traits and allocator_traits from the headers <iterator> and <memory>, respectively. Since traits are class templates, they can be specialized. Below an example of the specialization of iterator_traits for T*

template<T>
struct iterator_traits<T*>
{
    using difference_type   = std::ptrdiff_t;
    using value_type        = T;
    using pointer           = T*;
    using reference         = T&;
    using iterator_category = std::random_access_iterator_tag;
};

The expression std::iterator_traits<T>::value_type makes it possible to make generic code for full-fledged iterator classes usable even for raw pointers (since raw pointers don't have a member value_type).

Interaction between policies and traits

When writing your own generic libraries, it is important to think about ways users can specialize your own class templates. One has to be careful, however, not to let users fall victim to the One Definition Rule by using specializations of traits to inject rather than to extract behavior. To paraphrase this old post by Andrei Alexandrescu

The fundamental problem is that code that doesn't see the specialized version of a trait will still compile, is likely to link, and sometimes might even run. This is because in the absence of the explicit specialization, the non-specialized template kicks in, likely implementing a generic behavior that works for your special case as well. Consequently, if not all the code in an application sees the same definition of a trait, the ODR is violated.

The C++11 std::allocator_traits avoids these pitfalls by enforcing that all STL containers can only extract properties from their Allocator policies through std::allocator_traits<Allocator>. If users choose not to or forget to supply some of the required policy members, the traits class can step in and supply default values for those missing members. Because allocator_traits itself cannot be specialized, users always have to pass a fully defined allocator policy in order to customize their containers memory allocation, and no silent ODR violations can occur.

Note that as a library-writer, one can still specialize traits class templates (as the STL does in iterator_traits<T*>), but it is good practice to pass all user-defined specializations through policy classes into multi-valued traits that can extract the specialized behavior (as the STL does in allocator_traits<A>).

UPDATE: The ODR problems of user-defined specializations of traits classes happen mainly when traits are used as global class templates and you cannot guarantee that all future users will see all other user-defined specializations. Policies are local template parameters and contain all the relevant definitions, allowing them to be user-defined without interference in other code. Local template parameters that only contain type and constants -but no behaviorally functions- might still be called "traits" but they would not be visible to other code like the std::iterator_traits and std::allocator_traits.

这篇关于特质与政策有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆