gpt4 book ai didi

c++使用模板制作最通用的函数

转载 作者:行者123 更新时间:2023-11-28 00:22:46 25 4
gpt4 key购买 nike

我需要创建一个尽可能通用的函数,假设我有几个 map s 一起工作 --

int main()
{
map<int, string> m1 = {{0, "abc"}, {1, "def"}, {2, "ghi"}} ;
map<int, double> m2 = {{0, 0.5}, {1, 0.6}, {2, 0.7}} ;
map<int, vector<string>> m3 = {{0, {"abc", "def"}},
{1, {"ghi", "ijk"}}};

dosomething(m1);
dosomething(m2);
}

和我的 template d dosomething()函数看起来像这样——

template <typename A, typename B>
void dosomething(map<A,B> m)
{
for(auto e : m)
{
// do something with e
}
}

现在,如果我想编写一个适用于任何类型的 map 的函数(即 map<int, vector<string>map<int, string>map<int, double> )我该怎么做?如果我能做这样的事情那就太好了 --

template <typename A, typename B>
void dosomething(map<A,B> m)
{
for(auto me: m)
{
if (the type of B is not a standard container type)
{
// do something with me.second
}
else if(the type of B is a something from the standard container)
{
for(int i = 0 ; i < (me.second).size() ; i++)
// do something with me.second[i]
}
}
}

我如何在 C++ 中做到这一点?我假设编译器遵循 c++11 规范。

最佳答案

模板实例化中的代码是为了编译。因此,当根据模板参数的类型做不同的事情时,您通常不能将它们放在一个函数中。相反,您会将处理委托(delegate)给适当重载的函数,该函数本身可能是有条件适用的模板。例如,您的 dosomething()函数可能如下所示:

// suitable declaration/definition of dosomething_apply() go here; see below

template <typename A, typename B>
void dosomething(std::map<A, B> const& m) {
for (auto&& me: m) {
dosomething_apply(me.second);
}
}

顺便说一句:不要使用for (auto e: m)除非您有充分的理由需要此表格!在大多数情况下,这是一个性能问题。同样,您不应按值传递更大的对象,而应使用合适的引用类型传递它们。

有点棘手的事情是确定参数是否为 dosomething_apply()是否是标准容器:当然没有类型特征将所有标准分类为这样。不过,可以创建相应的类型特征。下一个问题是这是否真的是您想要检测的,因为您在元素上使用索引的假设布局意味着 me.second有一个基于索引的下标运算符,仅对 std::vector<...> 为真, std::dequeue<...> , std::basic_string<...> , std::array<...> , 和 std::bitset<...> (不确定这是否是提供下标的完整容器集)。还有其他容器不进行下标操作,例如std::list<...> , std::map<...> , std::unordered_map<...>

不过,所有这些容器(std::bitset<...> 除外)都提供迭代器接口(interface)。在 dosomething_apply() 的实现中检测一个类型是否提供迭代器接口(interface)并使用该接口(interface)可能更合理.由于您似乎设置为使用基于索引的下标,因此下面的示例处理了上面提到的类模板集。

第一步是创建一个合适的特征类,它检测需要特殊处理的所需类型。在许多情况下,可以检测到是否存在合适的成员函数或成员类型。由于您特别要求标准类型,因此有必要列出支持的类型,因为也可以检测非标准类的成员函数或成员类型。这是一个名为 is_subscripted 的示例特征类:

template <typename T>
struct is_subscripted: std::false_type {};

template <typename T, std::size_t N>
struct is_subscripted<std::array<T, N>>: std::true_type {};
template <std::size_t N>
struct is_subscripted<std::bitset<N>>: std::true_type {};
template <typename cT, typename Al>
struct is_subscripted<std::deque<cT, Al>>: std::true_type {};
template <typename cT, typename Tr, typename Al>
struct is_subscripted<std::basic_string<cT, Tr, Al>>: std::true_type {};
template <typename cT, typename Al>
struct is_subscripted<std::vector<cT, Al>>: std::true_type {};

它只是创建了一个默认版本的 trait,声明该 trait 与 std::false_type 的派生不匹配。 .然后相应的模板专门用于上面列出的类模板,每个模板都来自 std::true_type。 .这样一个形式的表达式 is_specialized<T>::value可用于检测特征是否适用于类型 T .

下一步是提供合适的处理函数。由于特征存在或不存在,使用 enable_if_t<...>是一个简单的方法:

template <typename T>
std::enable_if_t<!is_subscripted<T>::value>
dosomething_apply(T const& value) {
std::cout << "value=" << value << '\n';
}

template <typename T>
std::enable_if_t<is_subscripted<T>::value>
dosomething_apply(T const& range) {
for (auto size(range.size()), i(size - size); i != size; ++i) {
std::cout << "range=" << range[i] << '\n';
}
}

使用 std::enable_if_t<Value>Value评估为 true产量 void (使用第二个参数,例如 std::enable_if_t<Value, double> 以获得不同的类型)并且函数模板变得适用。与 Value 一起使用评估为 false不产生类型并且函数模板被忽略(有关正在发生的事情的解释,请参阅 SFINAE)。

... 就是这样。把一个合适的程序放在一起所需要的一切,一切都应该有效。下面是一个完整的程序,可以提供给 C++14 编译器。上面的代码中有一些 C++14 的次要用途,例如使用 std::enable_if_v<T>而不是 typename std::enable_if<T>::type .如果您需要通过适用于先前 C++ 标准的编译器传递代码,那么将 C++14 用法替换为 C++11 用法应该相对简单。

#include <map>
#include <iostream>
#include <utility>

#include <array>
#include <bitset>
#include <deque>
#include <string>
#include <vector>

// ----------------------------------------------------------------------------

template <typename T>
struct is_subscripted: std::false_type {};

template <typename T, std::size_t N>
struct is_subscripted<std::array<T, N>>: std::true_type {};
template <std::size_t N>
struct is_subscripted<std::bitset<N>>: std::true_type {};
template <typename cT, typename Al>
struct is_subscripted<std::deque<cT, Al>>: std::true_type {};
template <typename cT, typename Tr, typename Al>
struct is_subscripted<std::basic_string<cT, Tr, Al>>: std::true_type {};
template <typename cT, typename Al>
struct is_subscripted<std::vector<cT, Al>>: std::true_type {};

// ----------------------------------------------------------------------------

template <typename T>
std::enable_if_t<!is_subscripted<T>::value>
dosomething_apply(T const& value) {
std::cout << "value=" << value << '\n';
}

template <typename T>
std::enable_if_t<is_subscripted<T>::value>
dosomething_apply(T const& range) {
for (auto size(range.size()), i(size - size); i != size; ++i) {
std::cout << "range=" << range[i] << '\n';
}
}

// ----------------------------------------------------------------------------

template <typename A, typename B>
void dosomething(std::map<A, B> const& m)
{
for (auto&& me: m) {
dosomething_apply(me.second);
}
}

int main()
{
dosomething(std::map<int, int>{ { 1, 2 }, {3, 4 } });
dosomething(std::map<int, std::array<int, 2> >{ { 1, { { 2, 3 }} }, {4, { { 5, 6 } } } });
dosomething(std::map<int, std::bitset<4> >{ { 1, std::bitset<4>("1010") }, {2, std::bitset<4>("0011") } });
dosomething(std::map<int, std::deque<int>>{ { 10, { { 12, 13, 14 } } }, { 20, { 22, 23, 24 } } });
dosomething(std::map<int, std::string>{ { 1, "one" }, {2, "two" } });
dosomething(std::map<int, std::vector<int>>{ { 30, { { 32, 33, 34 } } }, { 40, { 42, 43, 44 } } });
}

关于c++使用模板制作最通用的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26643900/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com