What is the difference between a trait and a policy?

前端 未结 4 1397
天命终不由人
天命终不由人 2021-01-29 18:18

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

template ServerTraits;

Then later on I h

4条回答
  •  抹茶落季
    2021-01-29 18:50

    I think you will find the best possible answer to your question in this book by Andrei Alexandrescu. Here, I will try to give just a short overview. Hopefully it will help.


    A traits class is class that is usually intended to be a meta-function associating types to other types or to constant values to provide a characterization of those types. In other words, it is a way to model properties of types. The mechanism normally exploits templates and template specialization to define the association:

    template
    struct my_trait
    {
        typedef T& reference_type;
        static const bool isReference = false;
        // ... (possibly more properties here)
    };
    
    template<>
    struct my_trait
    {
        typedef T& reference_type;
        static const bool isReference = true;
        // ... (possibly more properties here)
    };
    

    The trait metafunction my_trait<> above associates the reference type T& and the constant Boolean value false to all types T which are not themselves references; on the other hand, it associates the reference type T& and the constant Boolean value true to all types T that are references.

    So for instance:

    int  -> reference_type = int&
            isReference = false
    
    int& -> reference_type = int&
            isReference = true
    

    In code, we could assert the above as follows (all the four lines below will compile, meaning that the condition expressed in the first argument to static_assert() is satisfied):

    static_assert(!(my_trait::isReference), "Error!");
    static_assert(  my_trait::isReference, "Error!");
    static_assert(
        std::is_same::reference_type, int&>::value, 
        "Error!"
         );
    static_assert(
        std::is_same::reference_type, int&>::value, 
        "Err!"
        );
    

    Here you could see I made use of the standard std::is_same<> template, which is itself a meta-function that accepts two, rather than one, type argument. Things can get arbitrarily complicated here.

    Although std::is_same<> is part of the type_traits header, some consider a class template to be a type traits class only if it acts as a meta-predicate (thus, accepting one template parameter). To the best of my knowledge, however, the terminology is not clearly defined.

    For an example of usage of a traits class in the C++ Standard Library, have a look at how the Input/Output Library and the String Library are designed.


    A policy is something slightly different (actually, pretty different). It is normally meant to be a class that specifies what the behavior of another, generic class should be regarding certain operations that could be potentially realized in several different ways (and whose implementation is, therefore, left up to the policy class).

    For instance, a generic smart pointer class could be designed as a template class that accepts a policy as a template parameter for deciding how to handle ref-counting - this is just a hypothetical, overly simplistic, and illustrative example, so please try to abstract from this concrete code and focus on the mechanism.

    That would allow the designer of the smart pointer to make no hard-coded commitment as to whether or not modifications of the reference counter shall be done in a thread-safe manner:

    template
    class smart_ptr : protected P
    {
    public:
        // ... 
        smart_ptr(smart_ptr const& sp)
            :
            p(sp.p),
            refcount(sp.refcount)
        {
            P::add_ref(refcount);
        }
        // ...
    private:
        T* p;
        int* refcount;
    };
    

    In a multi-threaded context, a client could use an instantiation of the smart pointer template with a policy that realizes thread-safe increments and decrements of the reference counter (Windows platformed assumed here):

    class mt_refcount_policy
    {
    protected:
        add_ref(int* refcount) { ::InterlockedIncrement(refcount); }
        release(int* refcount) { ::InterlockedDecrement(refcount); }
    };
    
    template
    using my_smart_ptr = smart_ptr;
    

    In a single-threaded environment, on the other hand, a client could instantiate the smart pointer template with a policy class that simply increases and decreases the counter's value:

    class st_refcount_policy
    {
    protected:
        add_ref(int* refcount) { (*refcount)++; }
        release(int* refcount) { (*refcount)--; }
    };
    
    template
    using my_smart_ptr = smart_ptr;
    

    This way, the library designer has provided a flexible solution that is capable of offering the best compromise between performance and safety ("You don't pay for what you don't use").

提交回复
热议问题