如何判断一个容器是否关联容器
如果你正在编写一个C++工具库,那么有可能需要知道一个容器是否关联容器。例如,你要写一个Contain()
工具函数,该函数用来判断元素是否在容器中。而且你希望对于不同的容器,总是使用性能最优的查找方式,也就是说,对于关联容器,使用find()
成员函数;而对于非关联容器,则使用std::find()
函数。
此时,你也许需要使用模板元编程。在STL中,所有关联容器都有一个特征:定义了key_type
成员类型,根据这个特征即可判断某个类型是否关联容器。
进行这个判断的模板元类型如下所示:
1 | template<typename C> |
使用方式:
1 | std::cout << IsAssociativeContainer<std::vector<int>>::Value; // false |
IsAssociativeContainer
的实现使用了C++模板的SFINAE技术,全称为“Substitution Failure Is Not An Error”,它的意思是:当编译器试图在多个重载的模板函数中查找最佳匹配时,如果某个函数的模板参数推导失败,那么这个函数会被丢弃,而不会出现编译错误。
首先,在IsAssociativeContainer
内部,定义了两个重载的Test()
模板函数,第一个重载带有T::key_type*
参数,对应关联容器类型,该函数返回true
;第二个重载接受可变参数,对应非关联容器类型,该函数返回false
。
然后,在定义静态常量Value
时,使用Test<C>(nullptr)
对其赋值。这里正是C++魔法起作用的时刻:编译器要在两个Test()
重载函数中找到最佳匹配,使用它的返回值作为Value
的值。假如模板类型C
不是关联容器,它没有key_type
成员类型,那么编译器在推导第一个重载的时候会失败,这个重载函数随即被丢弃,就像它从来没有出现过那样,所以编译器只能选择第二个重载。假如C
是关联容器,它具有key_type
成员类型,那么两个重载函数都能推导成功,此时编译器会优先选择第一个重载,因为接受可变参数的重载函数优先级是最低的。这种只接受可变参数的模板函数经常与SFINAE一起使用。