电子贺卡制作,徐州低价seo,大连建设工程设计院有限公司网站,企业系统管理平台相关系列文章 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)#xff1a;深入剖析 深入理解可变参数(va_list、std::initializer_list和可变参数模版) std::apply源码分析 目录
1.简介
2.std::ignore介绍
3.创建元组
3.1.… 相关系列文章 C之std::tuple(一) : 使用精讲(全) C三剑客之std::variant(一) : 使用 C三剑客之std::variant(二)深入剖析 深入理解可变参数(va_list、std::initializer_list和可变参数模版) std::apply源码分析 目录
1.简介
2.std::ignore介绍
3.创建元组
3.1.直接初始化方式
3.2.使用花括号初始化列表方式C11及以上版本
3.3.make_tuple方式
3.4.使用std::tie()函数方式
4.元素访问
4.1.std::get()方式
4.2.使用结构化绑定C17及以上
4.3.递归遍历元素
4.4.std::apply方式(C17及以上)
5.获取std::tuple的size
6.获取元组中的元素类型
7.std::forward_as_tuple
8.std::tuple_cat
9.std::swap
10.std::make_from_tuple
11.总结 1.简介 C11之后引入了std::tuple俗称元组元组(tuple)是一种用于组合多个不同类型的值的数据结构。元组可以将不同类型的数据打包在一起类似于一个容器可以按照索引顺序访问其中的元素。元组的大小在编译时确定不支持动态添加或移除元素。std::tuple的定义如下
templateclass... Types
class tuple; std::tuple类似互C语言的结构体不需要创建结构体而又有结构体的特征在某些情况下可以取代结构体而使得程序更加简洁直观。std::tuple理论上可以定义无数多个不同类型的成员变量。特别是你需要在函数之间返回多个值时或者需要一次性处理多个相关值时使用元组可以简化代码并提高可读性。
2.std::ignore介绍
在标头 tuple 定义任何值均可赋给而无效果的未指定类型的对象。目的是令 std::tie 在解包 std::tuple 时作为不使用的参数的占位符使用。例如解包 set.insert() 所返回的 pair 但只保存布尔值。
#include iostream
#include string
#include set
#include tupleint main()
{std::setstd::string set_of_str;bool inserted false;std::tie(std::ignore, inserted) set_of_str.insert(Test);if (inserted) {std::cout Value was inserted successfully\n;}
}
输出Value was inserted successfully
3.创建元组
3.1.直接初始化方式
//显示初始化
std::tuplebool, int, double, std::string a(true, 1, 3.0, 1112222);
3.2.使用花括号初始化列表方式C11及以上版本
//显示初始化
std::tuplebool, int, double, std::string a{true, 1, 3.0, 1112222};
3.3.make_tuple方式
//显示初始化
std::tuplebool, int, double, std::string a make_tuple(true, 1, 3.0, 1112222);//隐式初始化
auto b make_tuple(true, 1, 3.0, 1112222);
3.4.使用std::tie()函数方式 std::tie定义为
templateclass... Types
constexpr tupleTypes... tie (Types... args) noexcept;
std::tie生成一个tuple,此tuple包含的分量全部为实参的引用与make_tuple完全相反。主要用于从tuple中提取数据。例如bool myBool;
int myInt;
double myDouble;
std::string myString;std::tie(myBool, myInt, myDouble, myString) std::make_tuple(true, 1, 3.0, 1112222);
如果是要忽略某个特定的元素还可以使用第2章节的std::ignore来占位例如
bool myBool;
std::string myString;std::tie(myBool, std::ignore, std::ignore, myString) std::make_tuple(true, 1, 3.0, 1112222);
4.元素访问
4.1.std::getindex()方式
使用std::get来访问std::tuple特定的元素如
std::tuplebool, int, std::string a(true, 0, sfsfs);
bool b std::get0(a);
int c std::get1(a);
std::string d std::get2(a);std::get0(a) false;
std::get2(a) s344242;
4.2.使用结构化绑定C17及以上
在C17及以上版本中还可以使用结构化绑定 (structured bindings) 的方式来创建和访问元组可以更方便地访问和操作元组中的元素。结构化绑定允许直接从元组中提取元素并赋值给相应的变量。例如
std::tuplebool, int, std::string myTuple(true, false, Hello);
auto [a, b, c] myTuple;
这将自动创建变量a、b和c并将元组中相应位置的值赋给它们。
注意 元组是不可变的immutable一旦创建就不能更改其元素的值。但是可以通过解构赋值或使用std::getindex(tuple)来获取元组中的值并将新的值赋给它们从而修改元组中的值。 std::tuple不支持迭代器获取元素的值时只能通过元素索引或tie解包。给定的索引必须是在编译期间就已经确定的不能在运行期间动态传递否则会产生编译错误 4.3.递归遍历元素 由于 tuple 自身的原因无法直接遍历而 getindex 中 index 必须为运行前设置好的常数 所以 tuple 的遍历需要我们手写代码如下:
templateclass Tuple, std::size_t N
struct VisitTuple {static void Visit(const Tuple value) {VisitTupleTuple, N - 1::Visit(value);std::cout std::getN - 1(value);return void();}
};templateclass Tuple
struct VisitTupleTuple, 1 {static void Visit(const Tuple value) {std::cout std::get0(value);return void();}
};templateclass... Args
void TupleVisit(const std::tupleArgs... value) {VisitTupledecltype(value), sizeof ...(Args)::Visit(value);
}
4.4.std::apply方式(C17及以上)
利用可变参数的折叠表达式规则来访问std::tuple的元素例如
#include iostream
#include tuple
#include utilityint add(int first, int second) { return first second; }templatetypename T
T add_generic(T first, T second) { return first second; }auto add_lambda [](auto first, auto second) { return first second; };templatetypename... Ts
std::ostream operator(std::ostream os, std::tupleTs... const theTuple)
{std::apply([os](Ts const... tupleArgs){os [;std::size_t n{0};((os tupleArgs (n ! sizeof...(Ts) ? , : )), ...);os ];}, theTuple);return os;
}int main()
{// OKstd::cout std::apply(add, std::pair(1, 2)) \n;// 错误无法推导函数类型// std::cout std::apply(add_generic, std::make_pair(2.0f, 3.0f)) \n; // OKstd::cout std::apply(add_lambda, std::pair(2.0f, 3.0f)) \n; // 进阶示例std::tuple myTuple(25, Hello, 9.31f, c);std::cout myTuple \n;
}
输出
3
5
[25, Hello, 9.31, c] 上面语句((os tupleArgs (n ! sizeof...(Ts) ? , : )), ...);利用了C17的折叠表达式折叠表达式是C17新引进的语法特性。使用折叠表达式可以简化对C11中引入的参数包的处理从而在某些情况下避免使用递归。如果有不是很明白的地方可参考我的博客深入理解可变参数(va_list、std::initializer_list和可变参数模版)-CSDN博客 关于std::applay的使用有不明白的地方可以参考我的博客std::apply源码分析-CSDN博客
5.获取std::tuple的size
std::tuple_size的定义如下
template class... Types
struct tuple_size std::tupleTypes... : std::integral_constantstd::size_t, sizeof...(Types) { };
提供对 tuple 中元素数量的访问作为编译时常量表达式计算std::tuple的大小。例如
#include iostream
#include tupletemplate class T
void test(T value)
{int a[std::tuple_size_vT]; // 能用于编译时std::cout std::tuple_sizeT{} // 或运行时 sizeof a sizeof value \n;
}int main()
{test(std::make_tuple(1, 2, 3.14));
}
可能的输出3 12 16
6.获取元组中的元素类型
std::tuple_element定义如下
template std::size_t I, class... Types
class tuple_element I, tupleTypes... ;
可以使用std::tuple_elementindex, tuple::type来获取元组中特定索引位置的元素类型。
#include iostream
#include tupletemplate class... Args
struct type_list
{template std::size_t Nusing type typename std::tuple_elementN, std::tupleArgs...::type;
};int main()
{std::cout std::boolalpha;type_listint, char, bool::type2 x true;std::cout x \n;
}
输出true
7.std::forward_as_tuple
定义如下
template class... Types
tupleTypes... forward_as_tuple( Types... args ) noexcept;
template class... Types
constexpr tupleTypes... forward_as_tuple( Types... args ) noexcept;
用于接受右值引用数据生成 tuple, 与 std::make_tuple 不同的是它的右值是引用的当修改其值的时候原来赋值所用的右值也将修改实质上就是赋予了它地址。同std::tie一样也是生成一个全是引用的tuple,不过std::tie只接受左值而std::forward_as_tuple左值、右值都接受。主要是用于不损失类型属性的转发数据。
注意此处 tuple 内的类型应为引用否则相当于 std::make_tuple。例如
signed main(int argc, char *argv[]) {int a 123, c 456;float b 33.f, d .155;std::tupleint, float, int, float tu std::forward_as_tuple(a,b,c,d);std::get0 (tu) 2;std::get1 (tu) 4.5f;std::get2 (tu) 234;std::get3 (tu) 22.f;std::cout a std::endl; // 2std::cout b std::endl; // 4.5std::cout c std::endl; // 234std::cout d std::endl; // 22return 0;
}注意若参数是临时量则 forward_as_tuple 不延续其生存期必须在完整表达式结尾前使用它们。 8.std::tuple_cat 此函数接受多个tuple作为参数然后返回一个tuple。返回的这个tuple将tuple_cat的参数中的tuple的所有元素按所属的tuple在参数中的顺序以及其在tuple中的顺序排列成一个新的tuple。新tuple中元素的类型与参数中的tuple中的元素的类型完全一致。例如
#include iostream
#include string
#include tuple// 打印任何大小 tuple 的辅助函数
templateclass Tuple, std::size_t N
struct TuplePrinter
{static void print(const Tuple t){TuplePrinterTuple, N - 1::print(t);std::cout , std::getN-1(t);}
};templateclass Tuple
struct TuplePrinterTuple, 1
{static void print(const Tuple t) {std::cout std::get0(t);}
};templateclass... Args
void print(const std::tupleArgs... t)
{std::cout (;TuplePrinterdecltype(t), sizeof...(Args)::print(t);std::cout )\n;
}
// 辅助函数结束int main()
{std::tupleint, std::string, float t1(10, Test, 3.14);int n 7;auto t2 std::tuple_cat(t1, std::make_tuple(Foo, bar), t1, std::tie(n));n 10;print(t2);
}
输出(10, Test, 3.14, Foo, bar, 10, Test, 3.14, 10)
9.std::swap
交换两个std::tuple的内容前提是两个std::tuple的大小和元素类型必须相同例如
std::tupleint, double, char a1;
std::tupleint, double, char a2;
std::tupleunsigned int, double, char a3;
std::tupleint, std::string, char a4;
std::tupleint, double, char, std::string a5;
a1.swap(a2); //OK
a2.swap(a3); //编译出现error
a3.swap(a4);//编译出现error
a4.swap(a5);//编译出现error
上面a1和a2的大小和元素类型都相同因此可以交换。a2和a3、a3和a4、a4和a5类型不相同因此不能交换。我们再看一个std::tuple交换的例子
#include iostream
#include string
#include tupleint main()
{std::tupleint, std::string, floatp1{42, ABCD, 2.71},p2;p2 std::make_tuple(10, 1234, 3.14);auto print_p1_p2 [](auto rem) {std::cout rem p1 { std::get0(p1) , std::get1(p1) , std::get2(p1) }, p2 { std::get0(p2) , std::get1(p2) , std::get2(p2) }\n;};print_p1_p2(Before p1.swap(p2): );p1.swap(p2);print_p1_p2(After p1.swap(p2): );swap(p1, p2);print_p1_p2(After swap(p1, p2): );
}
输出
Before p1.swap(p2): p1 {42, ABCD, 2.71}, p2 {10, 1234, 3.14}
After p1.swap(p2): p1 {10, 1234, 3.14}, p2 {42, ABCD, 2.71}
After swap(p1, p2): p1 {42, ABCD, 2.71}, p2 {10, 1234, 3.14}
10.std::make_from_tuple
std::make_from_tuple是以元组std::tuple的元素作为构造函数的参数构造别的类型对象如下例子
#include iostream
#include tuplestruct Foo
{Foo(int first, float second, int third){std::cout first , second , third \n;}
};int main()
{auto tuple std::make_tuple(42, 3.14f, 0);std::make_from_tupleFoo(std::move(tuple));
}
输出42, 3.14, 0
11.总结
std::tuple 是一种重要的数据结构可以用于在函数参数之间传递数据也可以作为函数的返回值。在实际项目中我们可以灵活地使用 std::tuple以简化代码提高程序的性能。
后面我们将继续通过分析std::tuple源码的方式来更深层次讲解它的实现原理值得期待哦。。。
参考std::tuple - cppreference.com