3

经常,我想为不同的 STL 容器专门化一个函数。但是我不想一一专门化,因为它们中的一些共享大部分需要的接口,例如std::vector 和std::deque。

在我的用例中,主要有三类(vector-like、set-like、map-like)。例如,我想实现类似的东西,

template <class T>
struct A {
    template <class Y, class... Z>
    void func( Y y , Z... z ){
        //hypothetical static_if
        static_if ( T is similar to vector, deque, or boost::stable_vector etc which have push_back ) {
            t.push_back(y);
        }
        else static_if ( T is similar to set, unordered_set or boost::flat_set etc which have emplace)    {
            t.emplace(y);
        }

        else static_if ( T is similar to map, unordered_map or boost::flat_map etc which has emplace) {
            t.emplace(y, z...);
        }

    }

    T t;  
};

我认为这似乎是不可能的,但我希望这种情况有某种破解方法。如果它可以扩展到列表类型(std::list, std::forward_list, ...) 或 boost::heap 或其他,那也很好。然而,实现目标似乎太难了。

4

4 回答 4

4

不,谢谢。不要这样做。Scott Meyers 的Effective STL中有一个完整的部分专门介绍了这一点。以下摘录让您了解这是多么有问题:

那么,假设您渴望编写可与最常见的序列容器一起使用的代码:vector、deque 和 list。显然,您必须针对它们的能力交集进行编程,这意味着不使用储备或容量(参见条款 14),因为 deque 和 list 不提供它们。list 的存在也意味着你放弃了 operator[],并且你将自己限制在双向迭代器的能力上。反过来,这意味着您必须远离需要随机访问迭代器的算法,包括 sort、stable_sort、partial_sort 和 nth_element(参见条款 31)。

另一方面,您对支持向量的渴望排除了 push_front 和 pop_front 的使用,并且向量和双端队列都将 kibosh 放在拼接和排序的成员形式上。结合上面的限制,后一种禁止意味着没有任何形式的排序可以调用你的“广义序列容器”。

那是显而易见的东西。如果您违反任何这些限制,您的代码将无法使用至少一个您希望能够使用的容器进行编译。将编译的代码更加隐蔽。

罪魁祸首是适用于不同序列容器的迭代器、指针和引用失效的不同规则。要编写可正确使用向量、双端队列和列表的代码,您必须假定任何使这些容器中的迭代器、指针或引用无效的操作都会使您正在使用的容器中的它们无效。因此,您必须假定每次调用 insert 都会使所有内容无效,因为 deque::insert 会使所有迭代器无效,并且由于缺乏调用容量的能力,必须假定 vector::insert 会使所有指针和引用无效。(第 1 项解释了 deque 的独特之处在于有时会使其迭代器无效,而不会使其指针和引用无效。

想要更多?[是的,这个项目继续,继续,继续。]

于 2013-06-14T19:23:31.470 回答
1

这是一个用于容器的粗略类型特征库。

template<typename Container>
struct container_traits;

template<bool b=true>
struct has_emplace_back { typedef std::integral_constant<bool, b> emplace_back; };
template<bool b=true>
struct has_emplace { typedef std::integral_constant<bool, b> emplace; };

template<typename T, typename A>
struct container_traits< std::vector<T,A> > : has_emplace_back<>, has_emplace<> {};
// etc
template<typename T, typename A>
struct container_traits< std::set<T,A> > : has_emplace_back<false>, has_emplace<> {};
// etc

template<typename T>
using HasEmplaceBack = typename container_traits<T>::has_emplace_back;
template<typename T>
using HasEmplace = typename container_traits<T>::has_emplace;

template<int> struct enum_enum { enum class type {}; };
template<int index> using UniqueEnum = typename enum_enum<index>::type;

template<bool b, int index=1>
using EnableIf = typename std::enable_if< UniqueEnum<index> >::type;
template<bool b, int index=1>
using DisableIf = EnableIf< b, -index >;

template<typename Container, typename... Args, EnableIf< HasEmplace<Container>::value && !HasEmplaceBack<Container>::value, 1 >... >
void emplace_in( Container&& c, Args&&... args ) {
  std::forward<Container>(c).emplace( std::forward<Args>(args)... );
}
template<typename Container, typename... Args, EnableIf< HasEmplaceBack<Container>::value, 2 >... >
void emplace_in( Container&& c, Args&&... args ) {
  std::forward<Container>(c).emplace_back( std::forward<Args>(args)... );
}

EnableIf<>...技术在clang中不起作用,我没有编译它,所以它可能需要一些调试来修复。

于 2013-06-14T15:07:46.950 回答
0

这并非不可能,只是实施起来不方便或微不足道。真的,需要有一个container_traits图书馆。

这可以使用SFINAE来实现,以检测某个类型是否Container包含满足各种 STL容器概念要求的必要特性。

例如,is_associative_container可以使用 SFINAE 来检查该Container类型是否具有 akey_typemapped_typetypedef 和 a std::pair<const key_type, map_type>for ,以及实现该概念value_type的对象的所有其他要求。AssociativeContainer

无论如何,这确实需要作为一个成熟的库来实现。实施起来并非易事,如果您有简单的一次性需求,您可能应该寻找另一种解决方案。您最好在该Iterator级别进行抽象。

于 2013-06-14T14:43:02.570 回答
0

一周前我尝试了复杂的特征,但为了更好的可读性。

因此,请参见此处的示例:是否有更好的方法来检查 STL 容器是否为多 * 容器

于 2013-06-14T18:14:05.187 回答