c++ template class; function with arbitrary container type, how to define it?

前端 未结 3 619
名媛妹妹
名媛妹妹 2020-12-04 18:25

Okay, simple template question. Say I define my template class something like this:

template
class foo {
public:
    foo(T const& first         


        
3条回答
  •  执笔经年
    2020-12-04 18:49


    Traits solution.

    Generalize not more than needed, and not less.

    In some cases that solution might not be enough as it will match any template with such signature (e.g. shared_ptr), in which case you could make use of type_traits, very much like duck-typing (templates are duck typed in general).

    #include 
    
    // Helper to determine whether there's a const_iterator for T.
    template
    struct has_const_iterator
    {
    private:
        template static char test(typename C::const_iterator*);
        template static int  test(...);
    public:
        enum { value = sizeof(test(0)) == sizeof(char) };
    };
    
    
    // bar() is defined for Containers that define const_iterator as well
    // as value_type.
    template 
    typename std::enable_if::value,
                            void>::type
    bar(const Container &c, typename Container::value_type const & t)
    {
      // Note: no extra check needed for value_type, the check comes for
      //       free in the function signature already.
    }
    
    
    template 
    class DoesNotHaveConstIterator {};
    
    #include 
    int main () {
        std::vector c;
        bar (c, 1.2f);
    
        DoesNotHaveConstIterator b;
        bar (b, 1.2f); // correctly fails to compile
    }
    

    A good template usually does not artificially restrict the kind of types for which they are valid (why should they?). But imagine in the example above you need to have access to an objects const_iterator, then you can use SFINAE and type_traits to put those constraints on your function.


    Or just to as the standard library does

    Generalize not more than needed, and not less.

    template 
    void bar (Iter it, Iter end) {
        for (; it!=end; ++it) { /*...*/ }
    }
    
    #include 
    int main () {
        std::vector c;
        bar (c.begin(), c.end());
    }
    

    For more such examples, look into .

    This approach's strength is its simplicity and is based on concepts like ForwardIterator. It will even work for arrays. If you want to report errors right in the signature, you can combine it with traits.


    std containers with signature like std::vector (not recommended)

    The simplest solution is approximated by Kerrek SB already, though it is invalid C++. The corrected variant goes like so:

    #include  // for std::allocator
    template