C++ templates that accept only certain types

前端 未结 14 2576
一整个雨季
一整个雨季 2020-11-22 11:37

In Java you can define generic class that accept only types that extends class of your choice, eg:

public class ObservableList {
  ...
         


        
14条回答
  •  一生所求
    2020-11-22 11:59

    This typically is unwarranted in C++, as other answers here have noted. In C++ we tend to define generic types based on other constraints other than "inherits from this class". If you really wanted to do that, it's quite easy to do in C++11 and :

    #include 
    
    template
    class observable_list {
        static_assert(std::is_base_of::value, "T must inherit from list");
        // code here..
    };
    

    This breaks a lot of the concepts that people expect in C++ though. It's better to use tricks like defining your own traits. For example, maybe observable_list wants to accept any type of container that has the typedefs const_iterator and a begin and end member function that returns const_iterator. If you restrict this to classes that inherit from list then a user who has their own type that doesn't inherit from list but provides these member functions and typedefs would be unable to use your observable_list.

    There are two solutions to this issue, one of them is to not constrain anything and rely on duck typing. A big con to this solution is that it involves a massive amount of errors that can be hard for users to grok. Another solution is to define traits to constrain the type provided to meet the interface requirements. The big con for this solution is that involves extra writing which can be seen as annoying. However, the positive side is that you will be able to write your own error messages a la static_assert.

    For completeness, the solution to the example above is given:

    #include 
    
    template
    struct void_ {
        using type = void;
    };
    
    template
    using Void = typename void_::type;
    
    template
    struct has_const_iterator : std::false_type {};
    
    template
    struct has_const_iterator> : std::true_type {};
    
    struct has_begin_end_impl {
        template().begin()),
                             typename End   = decltype(std::declval().end())>
        static std::true_type test(int);
        template
        static std::false_type test(...);
    };
    
    template
    struct has_begin_end : decltype(has_begin_end_impl::test(0)) {};
    
    template
    class observable_list {
        static_assert(has_const_iterator::value, "Must have a const_iterator typedef");
        static_assert(has_begin_end::value, "Must have begin and end member functions");
        // code here...
    };
    

    There are a lot of concepts shown in the example above that showcase C++11's features. Some search terms for the curious are variadic templates, SFINAE, expression SFINAE, and type traits.

提交回复
热议问题