C ++是否可以编写一个以Container作为参数的模板?

时间:2014-12-31 08:45:39

标签: c++11 c++14

我会尝试用一个例子说明我的观点:

我们有

template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);

但我在想是否可以让它更方便:

template <typename T> void sort(std::vector<T>& container) {
    std::sort( container.begin(), container.end() );
}
template <typename T> void sort(std::list<T>& container);
template <typename T> void sort(std::array<T>& container);
//e.t.c

您知道有很多容器类型,可以为所有容器类型编码一次吗?

void sort(ContainerType<ElementType> &container); 
//and container should have begin() and end() methods,
//otherwise the compiler would warn me.

4 个答案:

答案 0 :(得分:3)

您在谈论C ++中的 concepts 。这个想法目前已经讨论了很长时间,但它们仍然没有达到标准。 See here

template<Sortable Cont>
void sort(Cont& container);

目前这项工作已接近尾声,一些实验性实施是already available,我们希望它们能够达到C ++ 17。关于概念最好的事情是它们直截了当的错误信息:

list<int> lst = ...;   // oops, bidirectional iterators
sort(lst);             // error: 'T' is not a/an 'Sortable' type

在现代编译器中,与模板化代码相关的错误非常混乱。与此示例相比,使用Visual Studio 2013编译:

std::list<int> l;
std::sort(l.begin(), l.end());
// error C2784: 'unknown-type std::operator -(std::move_iterator<_RanIt> &,const std::move_iterator<_RanIt2> &)' : could not deduce template argument for 'std::move_iterator<_RanIt> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
// error C2784: 'unknown-type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : could not deduce template argument for 'const std::reverse_iterator<_RanIt> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
// error C2784: 'unknown-type std::operator -(const std::_Revranit<_RanIt,_Base> &,const std::_Revranit<_RanIt2,_Base2> &)' : could not deduce template argument for 'const std::_Revranit<_RanIt,_Base> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'

SO上甚至还有一个标记:c++-concepts

答案 1 :(得分:1)

很容易编写适用于任何容器的排序函数。只需写下:

template<class C>
void sort(C& container) {
    std::sort( container.begin(), container.end() );
}

但是,如果您希望仅为容器选择排序功能,则会变得有点困难:由于概念尚不可用,您必须为所有容器编写自己的类型特征并使用SFINAE。


编辑:想想看,因为任何具有随机访问迭代器的类都可能是一个容器,它应该足以编写以下内容(不需要容器特征):

#include <type_traits>
template<class C>
typename std::enable_if<std::is_same<typename std::iterator_traits<typename C::iterator>::iterator_category, std::random_access_iterator_tag>{}>::type
sort(C& container) {
    std::sort( container.begin(), container.end() );
}

答案 2 :(得分:0)

#include <iterator>
#include <utility>
#include <type_traits>

namespace detail
{
    using std::begin;    
    template <typename T, typename = void>
    struct has_begin : std::false_type {};
    template <typename T>
    struct has_begin<T, decltype(void(begin(std::declval<T&>())))> : std::true_type {};

    using std::end;
    template <typename T, typename = void>
    struct has_end : std::false_type {};
    template <typename T>
    struct has_end<T, decltype(void(end(std::declval<T&>())))> : std::true_type {};
}

template <typename T> using has_begin = detail::has_begin<T>;
template <typename T> using has_end = detail::has_end<T>;

用法:

template <typename ContainerType>
void sort(ContainerType& container)
{
    static_assert(has_begin<ContainerType>{} && has_end<ContainerType>{},
                  "Invalid container type");
}

试验:

#include <vector>
#include <list>

namespace X
{
    struct A {};        
    A* begin(A&) { return {}; }
    A* end(A&) { return {}; }
}

struct B {};

int main()
{
    std::vector<int> v; sort(v);  // OK
    std::list<int> l; sort(l);    // OK
    X::A a; sort(a);              // OK
    int arr[3]{}; sort(arr);      // OK
    B b; sort(b);                 // error: Invalid container type
}

DEMO

答案 3 :(得分:0)

在回答这个问题时,MikeMB的回答给出了方法,但没有编译。但这是我的尝试。一种更为复杂的方法。你必须重载SortHelper才能接受比较器。

#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <algorithm>


template<typename C>
void SortHelper(C& container, std::random_access_iterator_tag)
{
    std::sort(std::begin(container), std::end(container));
}

template<typename C>
void SortHelper(C& container, std::bidirectional_iterator_tag)
{
    container.sort();
}



template<class C>
void sort(C& container)
{
    SortHelper(container, typename std::iterator_traits<typename C::iterator>::iterator_category());
}


int main()
{
    std::vector<int> ints1 { 3, 2, 1 };
    std::list<int> ints2 { 3, 2, 1 };

    sort(ints1);
    sort(ints2);

    std::cout << "printing ints1\n";
    for (auto e : ints1 ) { std::cout << e << "\n" ; }
    std::cout << "printing ints2\n";
    for (auto e : ints2 ) { std::cout << e << "\n" ; }

}

<强> 输出

印刷ints1
1
2
3
印刷ints2
1
2
3