tuple是一个固定大小的不同类型值的集合,是泛化的std::pair。和c#中的tuple类似,但是比c#中的tuple强大得多。我们也可以把他当做一个通用的结构体来用,不需要创建结构体又获取结构体的特征,在某些情况下可以取代结构体使程序更简洁,直观。
构造一个tuple
tuple<const char*, int>tp = make_tuple(sendPack,nSendSize); //构造一个tuple
这个tuple等价于一个结构体
struct A { char* p; int len; };
用tuple<const char*, int>tp就可以不用创建这个结构体了,而作用是一样的,是不是更简洁直观了。还有一种方法也可以创建元组,用std::tie,它会创建一个元组的左值引用。
auto tp = return std::tie(1, "aa", 2); //tp的类型实际是: std::tuple<int&,string&, int&>
再看看如何获取它的值:
const char* data = tp.get<0>(); //获取第一个值 int len = tp.get<1>(); //获取第二个值
还有一种方法也可以获取元组的值,通过std::tie解包tuple
int x,y; string a; std::tie(x,a,y) = tp;
通过tie解包后,tp中三个值会自动赋值给三个变量。
解包时,我们如果只想解某个位置的值时,可以用std::ignore占位符来表示不解某个位置的值。比如我们只想解第三个值时:
std::tie(std::ignore,std::ignore,y) = tp; //只解第三个值了
还有一个创建右值的引用元组方法:forward_as_tuple。
std::map<int, std::string> m; m.emplace(std::forward_as_tuple(10, std::string(20, 'a')));
它实际上创建了一个类似于std::tuple<int&&, std::string&&>类型的tuple。
我们还可以通过tuple_cat连接多个tupe
int main() { std::tuple<int, std::string, float> t1(10, "Test", 3.14); int n = 7; auto t2 = std::tuple_cat(t1, std::make_pair("Foo", "bar"), t1, std::tie(n)); n = 10; print(t2); }
输出结果:
(10, Test, 3.14, Foo, bar, 10, Test, 3.14, 10)
到这里tuple的用法介绍完了,是不是很简单,也很容易使用,相信你使用它之后就离不开它了。我前面说过tuple是简约而不简单。它有很多高级的用法。它和模板元关系密切,要介绍它的高级用法的时候,读者需要一定的模板元基础,如果你只是把它当一个泛型的pair去使用时,这部分可以不看,如果你想用它高级用法的时候就往下看。让我们要慢慢揭开tuple神秘的面纱。
通过std::tuple_element获取元素类型。
template<typename Tuple> void Fun(Tuple& tp) { std::tuple_element<0,Tuple>::type first = std::get<0> (mytuple); std::tuple_element<1,Tuple>::type second = std::get<1> (mytuple); }
获取tuple中元素的个数:
tuple t;
int size = std::tuple_size<decltype(t))>::value;
因为tuple的参数是变长的,也没有for_each函数,如果我们想遍历tuple中的每个元素,需要自己写代码实现。比如我要打印tuple中的每个元素。
template<class Tuple, std::size_t N> struct TuplePrinter { static void print(const Tuple& t) { TuplePrinter<Tuple, N - 1>::print(t); std::cout << ", " << std::get<N - 1>(t); } }; template<class Tuple> struct TuplePrinter<Tuple, 1>{ static void print(const Tuple& t) { std::cout << std::get<0>(t); } }; template<class... Args> void PrintTuple(const std::tuple<Args...>& t) { std::cout << "("; TuplePrinter<decltype(t), sizeof...(Args)>::print(t); std::cout << ")\n"; }
namespace detail { template<int I, typename T, typename... Args> struct find_index { static int call(std::tuple<Args...> const& t, T&& val) { return (std::get<I - 1>(t) == val) ? I - 1 : find_index<I - 1, T, Args...>::call(t, std::forward<T>(val)); } }; template<typename T, typename... Args> struct find_index<0, T, Args...> { static int call(std::tuple<Args...> const& t, T&& val) { return (std::get<0>(t) == val) ? 0 : -1; } }; } template<typename T, typename... Args> int find_index(std::tuple<Args...> const& t, T&& val) { return detail::find_index<0, sizeof...(Args) - 1, T, Args...>:: call(t, std::forward<T>(val)); } int main() { std::tuple<int, int, int, int> a(2, 3, 1, 4); std::cout << find_index(a, 1) << std::endl; // Prints 2 std::cout << find_index(a, 2) << std::endl; // Prints 0 std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found) }
#include <tuple> #include <type_traits> #include <utility> template<size_t N> struct Apply { template<typename F, typename T, typename... A> static inline auto apply(F && f, T && t, A &&... a) -> decltype(Apply<N-1>::apply( ::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... )) { return Apply<N-1>::apply(::std::forward<F>(f), ::std::forward<T>(t), ::std::get<N-1>(::std::forward<T>(t)), ::std::forward<A>(a)... ); } }; template<> struct Apply<0> { template<typename F, typename T, typename... A> static inline auto apply(F && f, T &&, A &&... a) -> decltype(::std::forward<F>(f) (::std::forward<A>(a)...)) { return ::std::forward<F>(f)(::std::forward<A> (a)...); } }; template<typename F, typename T> inline auto apply(F && f, T && t) -> decltype(Apply< ::std::tuple_size< typename ::std::decay<T>::type >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t))) { return Apply< ::std::tuple_size< typename ::std::decay<T>::type >::value>::apply(::std::forward<F>(f), ::std::forward<T>(t)); } void one(int i, double d) { std::cout << "function one(" << i << ", " << d << ");\n"; } int two(int i) { std::cout << "function two(" << i << ");\n"; return i; } //测试代码 int main() { std::tuple<int, double> tup(23, 4.5); apply(one, tup); int d = apply(two, std::make_tuple(2)); return 0; }
看到这里,想必大家对tuple有了一个全面的认识了吧,怎么样,它是简约而不简单吧。对模板元不熟悉的童鞋可以不看tuple高级用法部分,不要为看不懂而捉急,没事的,高级部分一般用不到,知道基本用法就够用了。
tuple和vector比较:
vector只能容纳同一种类型的数据,tuple可以容纳任意类型的数据;
vector和variant比较:
二者都可以容纳不同类型的数据,但是variant的类型个数是固定的,而tuple的类型个数不是固定的,是变长的,更为强大。
The decltype operator yields the type of a specified expression_r. The decltype operator, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a template function whose return type depends on the types of its template arguments. Or, use auto and decltype to declare a template function that wraps a call to another function, and then returns the return type of the wrapped function.
#include <iostream>
#include <string>
#include <utility>
#include <iomanip>
int var;
const int&& fx();
struct A {
};
const A* a = new A();
decltype(fx()) aa = 20;
decltype(var) ab = 10;
decltype(a->x) ac = 1.0;
decltype((a->x)) ad = 2.0;
template<typename T, typename U>
auto myFunc(T&& t, U&& u) -> decltype (forward<T>(t) + forward<U>(u))
template<typename T1, typename T2>
auto Plus(T1&& t1, T2&& t2) ->
{
}
class X
{
public:
private:
};
int main(int argc,char** argv){
}
在C++中经常要用到很长的变量名,如果已经有变量和你将使用的变量是一个类型,即可使用decltype关键字
来申明一样的类型变量。
decltype原理
返回现有变量类型,decltype是一个关键字,而不是一个函数,这有啥区别呢?decltype在编译阶段返回变量类
型,而不是在运行阶段传递不同变量返回不同值。
decltype使用范例
1、复杂已知变量类型
map<string, vector<string>> str_map;
decltype(str_map) str_map_new;
2、表达式返回值类型
int a, b;
decltype(a + b) a;
3、函数返回值
int foo(int i) {
return i;
}
double foo(double d) {
return d;
}
template<typename T>
auto getNum(T t)->decltype(foo(t)) {
return foo(t);
}
注意
1、decltype两个括号返回变量引用类型
int i;
decltype((i)) r = i;
decltype(i) a;
2、auto和decltype配合使用可以实现不同返回类型
返回值 decltype(表达式)
[返回值的类型是表达式参数的类型]
这个可也用来决定表达式的类型,就像Bjarne暗示的一样,如果我们需要去初始化某种类型的变量,auto是最简单的选择,但是如果我们所需的类型不是一个变量,例如返回值这时我们可也试一下decltype。
现在我们回看一些例子我们先前做过的,
template <class U, class V>
void Somefunction(U u, V v)
{
result = u*v;//now what type would be the result???
decltype(u*v) result = u*v;//Hmm .... we got what we want
}
在下面的一个段落我将会让你熟悉这个观念用 auto 和 decltype 来声明模板函数的返回值,其类型依靠模板参数。
1. 如果这个表达式是个函数,decltype 给出的类型为函数返回值的类型。
int add(int i, int j){ return i+j; }
decltype(add(5,6)) var = 5;//Here the type of var is return of add() -> which is int
2.如果表达式是一个左值类型,那么 decltype 给出的类型为表达式左值引用类型。
struct M { double x; };
double pi = 3.14;
const M* m = new M();
decltype( (m->x) ) piRef = pi;
// Note: Due to the inner bracets the inner statement is evaluated as expression,
// rather than member 'x' and as type of x is double and as this is lvale
// the return of declspec is double& and as 'm' is a const pointer
// the return is actually const double&.
// So the type of piRef is const double&
3.非常重要的标记一下,decltype 不会执行表达式而auto会,他仅仅推论一下表达式的类型。
int foo(){}
decltype( foo() ) x; // x is an int and note that
// foo() is not actually called at runtime
跟踪返回类型:
这对 C++ 开发者来说是一个全新的特性,直到现在函数的返回类型必须放在函数名的前面。到了 C++11,我们也可以将函数返回值的类型放在函数声明后,当然仅需要用 auto 替代返回类型。现在我们想知道怎么做,让我们来寻找答案:
template<class U, class V>
??? Multiply(U u, V v) // how to specifiy the type of the return value
{
return u*v;
}
我们明显的不能像这样:
template<class U, class V>
decltype(u*v) Multiply(U u, V v) // Because u & v are not defined before Multiply.
// What to do...what to do !!!
{
return u*v;
}
这种情况我们可也使用 auto 然后当我们使用 decltype(u*v) 作为返回值这个类型便知晓了.
这是不是很酷?
最近群里比较热闹,大家都在山寨c++11的std::bind,三位童孩分别实现了自己的bind,代码分别在这里:
这些实现思路和ms stl的std::bind的实现思路是差不多的,只是在实现的细节上有些不同。个人觉得木头云的实现更简洁,本文中的简单实现也是基于木头云的bind之上的,在此表示感谢。下面我们来分析一下bind的基本原理。
bind的思想实际上是一种延迟计算的思想,将可调用对象保存起来,然后在需要的时候再调用。而且这种绑定是非常灵活的,不论是普通函数、函数对象、还是成员函数都可以绑定,而且其参数可以支持占位符,比如你可以这样绑定一个二元函数auto f = bind(&func, _1, _2);,调用的时候通过f(1,2)实现调用。关于bind的用法更多的介绍可以参考我博客中介绍:http://www.cnblogs.com/qicosmos/p/3302144.html。
要实现一个bind需要解决两个问题,第一个是保存可调用对象及其形参,第二个是如何实现调用。下面来分析如何解决这两个问题。
实现bind的首先要解决的问题是如何将可调用对象保存起来,以便在后面调用。要保存可调用对象,需要保存两个东西,一个是可调用对象的实例,另一个是可调用对象的形参。保存可调用对象的实例相很简单,因为bind时直接要传这个可调用对象的,将其作为一个成员变量即可。而保存可调用对象的形参就麻烦一点,因为这个形参是变参,不能直接将变参作为成员变量。如果要保存变参的话,我们需要用tuple来将变参保存起来。
bind的形参因为是变参,可以是0个,也可能是多个,大部分情况下是占位符,还有可能占位符和实参都有。正是由于bind绑定的灵活性,导致我们不得不在调用的时候需要找出哪些是占位符,哪些是实参。如果某个一参数是实参我们就不处理,如果是占位符,我们就要将这个占位符替换为对应的实参。比如我们绑定了一个三元函数:auto f = bind(&func, _1, 2, _2);调用时f(1,3);由于绑定时有三个参数,一个实参,两个占位符,调用时传入了两个实参,这时我们就要将占位符_1替换为实参1,占位符_2替换为实参3。这个占位符的替换需要按照调用实参的顺序来替换,如果调用时的实参个数比占位符要多,则忽略多余的实参。
调用的实参,我们也会先将其转换为tuple,用于在后面去替换占位符时,选取合适的实参。
前面讲到绑定可调用对象时,将可调用对象的形参(可能含占位符)保存起来,保存到tuple中了。到了调用阶段,我们就要反过来将tuple展开为可变参数,因为这个可变参数才是可调用对象的形参,否则就无法实现调用了。这里我们会借助于一个整形序列来将tuple变为可变参数,在展开tuple的过程中我们还需要根据占位符来选择合适实参,即占位符要替换为调用实参。
这个地方比较关键,因为tuple中可能含有占位符,我们展开tuple时,如果发现某个元素类型为占位符,则从调用的实参生成的tuple中取出一个实参,用来作为变参的一个参数;当某个类型不为占位符时,则直接从绑定时生成的形参tuple中取出参数,用来作为变参的一个参数。最终tuple被展开为一个变参列表,这时,这个列表中没有占位符了,全是实参,就可以实现调用了。这里还有一个细节要注意,替换占位符的时候,如何从tuple中选择合适的参数呢,因为替换的时候要根据顺序来选择。这里是通过占位符的模板参数I来选择,因为占位符place_holder<I>的实例_1实际上place_holder<1>, 占位符实例_2实际上是palce_holder<2>,我们是可以根据占位符的模板参数来获取其顺序的。
#include <tuple> #include <type_traits> using namespace std; template<int...> struct IndexTuple{}; template<int N, int... Indexes> struct MakeIndexes : MakeIndexes<N - 1, N - 1, Indexes...>{}; template<int... indexes> struct MakeIndexes<0, indexes...> { typedef IndexTuple<indexes...> type; }; template <int I> struct Placeholder { }; Placeholder<1> _1; Placeholder<2> _2; Placeholder<3> _3; Placeholder<4> _4; Placeholder<5> _5; Placeholder<6> _6; Placeholder<7> _7; Placeholder<8> _8; Placeholder<9> _9; Placeholder<10> _10; // result type traits template <typename F> struct result_traits : result_traits<decltype(&F::operator())> {}; template <typename T> struct result_traits<T*> : result_traits<T> {}; /* check function */ template <typename R, typename... P> struct result_traits<R(*)(P...)> { typedef R type; }; /* check member function */ template <typename R, typename C, typename... P> struct result_traits<R(C::*)(P...)> { typedef R type; }; template <typename T, class Tuple> inline auto select(T&& val, Tuple&)->T&& { return std::forward<T>(val); } template <int I, class Tuple> inline auto select(Placeholder<I>&, Tuple& tp) -> decltype(std::get<I - 1>(tp)) { return std::get<I - 1>(tp); } // The invoker for call a callable template <typename T> struct is_pointer_noref : std::is_pointer<typename std::remove_reference<T>::type> {}; template <typename T> struct is_memfunc_noref : std::is_member_function_pointer<typename std::remove_reference<T>::type> {}; template <typename R, typename F, typename... P> inline typename std::enable_if<is_pointer_noref<F>::value, R>::type invoke(F&& f, P&&... par) { return (*std::forward<F>(f))(std::forward<P>(par)...); } template <typename R, typename F, typename P1, typename... P> inline typename std::enable_if<is_memfunc_noref<F>::value && is_pointer_noref<P1>::value, R>::type invoke(F&& f, P1&& this_ptr, P&&... par) { return (std::forward<P1>(this_ptr)->*std::forward<F>(f))(std::forward<P>(par)...); } template <typename R, typename F, typename P1, typename... P> inline typename std::enable_if<is_memfunc_noref<F>::value && !is_pointer_noref<P1>::value, R>::type invoke(F&& f, P1&& this_obj, P&&... par) { return (std::forward<P1>(this_obj).*std::forward<F>(f))(std::forward<P>(par)...); } template <typename R, typename F, typename... P> inline typename std::enable_if<!is_pointer_noref<F>::value && !is_memfunc_noref<F>::value, R>::type invoke(F&& f, P&&... par) { return std::forward<F>(f)(std::forward<P>(par)...); } template<typename Fun, typename... Args> struct Bind_t { typedef typename decay<Fun>::type FunType; typedef std::tuple<typename decay<Args>::type...> ArgType; typedef typename result_traits<FunType>::type result_type; public: template<class F, class... BArgs> Bind_t(F& f, BArgs&... args) : m_func(f), m_args(args...) { } template<typename F, typename... BArgs> Bind_t(F&& f, BArgs&&... par) : m_func(std::move(f)), m_args(std::move(par)...) {} template <typename... CArgs> result_type operator()(CArgs&&... args) { return do_call(MakeIndexes<std::tuple_size<ArgType>::value>::type(), std::forward_as_tuple(std::forward<CArgs>(args)...)); } template<typename ArgTuple, int... Indexes > result_type do_call(IndexTuple< Indexes... >& in, ArgTuple& argtp) { return simple::invoke<result_type>(m_func, select(std::get<Indexes>(m_args), argtp)...); //return m_func(select(std::get<Indexes>(m_args), argtp)...); } private: FunType m_func; ArgType m_args; }; template <typename F, typename... P> inline Bind_t<F, P...> Bind(F&& f, P&&... par) { return Bind_t<F, P...>(std::forward<F>(f), std::forward<P>(par)...); } template <typename F, typename... P> inline Bind_t<F, P...> Bind(F& f, P&... par) { return Bind_t<F, P...>(f, par...); }View Code
测试代码:
void TestFun1(int a, int b, int c) { } void TestBind1() { Bind(&TestFun1, _1, _2, _3)(1, 2, 3); Bind(&TestFun1, 4, 5, _1)(6); Bind(&TestFun1, _1, 4, 5)(3); Bind(&TestFun1, 3, _1, 5)(4); }View Code
由于只是展示bind实现的关键技术,很多的实现细节并没有处理,比如参数是否是引用、右值、const volotile、绑定非静态的成员变量都还没处理,仅仅供学习之用,并非是重复发明轮子,只是展示bind是如何实现, 实际项目中还是使用c++11的std::bind为好。null同学还图文并茂的介绍了bind的过程:http://www.cnblogs.com/xusd-null/p/3698969.html,有兴趣的童孩可以看看.
在实际使用过程中,我更喜欢使用lambda表达式,因为lambda表达式使用起来更简单直观,lambda表达式在绝大多数情况下可以替代bind。
如果你觉得这篇文章对你有用,可以点一下推荐,谢谢。
文章浏览阅读101次。4.class可以有⽆参的构造函数,struct不可以,必须是有参的构造函数,⽽且在有参的构造函数必须初始。2.Struct适⽤于作为经常使⽤的⼀些数据组合成的新类型,表示诸如点、矩形等主要⽤来存储数据的轻量。1.Class⽐较适合⼤的和复杂的数据,表现抽象和多级别的对象层次时。2.class允许继承、被继承,struct不允许,只能继承接⼝。3.Struct有性能优势,Class有⾯向对象的扩展优势。3.class可以初始化变量,struct不可以。1.class是引⽤类型,struct是值类型。
文章浏览阅读586次。想实现的功能是点击顶部按钮之后按关键字进行搜索,已经可以从服务器收到反馈的json信息,但从json信息的解析开始就会闪退,加载listview也不知道行不行public abstract class loadlistview{public ListView plv;public String js;public int listlength;public int listvisit;public..._rton转json为什么会闪退
文章浏览阅读219次。如何使用wordnet词典,得到英文句子的同义句_get_synonyms wordnet
文章浏览阅读521次。系统项目报表导出 导出任务队列表 + 定时扫描 + 多线程_积木报表 多线程
文章浏览阅读1.1k次,点赞9次,收藏9次。使用AJAX技术的好处之一是它能够提供更好的用户体验,因为它允许在不重新加载整个页面的情况下更新网页的某一部分。另外,AJAX还使得开发人员能够创建更复杂、更动态的Web应用程序,因为它们可以在后台与服务器进行通信,而不需要打断用户的浏览体验。在Web开发中,AJAX(Asynchronous JavaScript and XML)是一种常用的技术,用于在不重新加载整个页面的情况下,从服务器获取数据并更新网页的某一部分。使用AJAX,你可以创建异步请求,从而提供更快的响应和更好的用户体验。_ajax 获取http数据
文章浏览阅读2.8k次。登录退出、修改密码、关机重启_字符终端
文章浏览阅读3.8k次,点赞3次,收藏51次。前段时间看到一位发烧友制作的超声波雷达扫描神器,用到了Arduino和Processing,可惜啊,我不会Processing更看不懂人家的程序,咋办呢?嘿嘿,所以我就换了个思路解决,因为我会一点Python啊,那就动手吧!在做这个案例之前先要搞明白一个问题:怎么将Arduino通过超声波检测到的距离反馈到Python端?这个嘛,我首先想到了串行通信接口。没错!就是串口。只要Arduino将数据发送给COM口,然后Python能从COM口读取到这个数据就可以啦!我先写了一个测试程序试了一下,OK!搞定_超声波扫描建模 python库
文章浏览阅读4.2k次。端—端加密指信息由发送端自动加密,并且由TCP/IP进行数据包封装,然后作为不可阅读和不可识别的数据穿过互联网,当这些信息到达目的地,将被自动重组、解密,而成为可读的数据。不可逆加密算法的特征是加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,只有重新输入明文,并再次经过同样不可逆的加密算法处理,得到相同的加密密文并被系统重新识别后,才能真正解密。2.使用时,加密者查找明文字母表中需要加密的消息中的每一个字母所在位置,并且写下密文字母表中对应的字母。_凯撒加密
文章浏览阅读5.7k次。CIP报文解析常用到的几个字段:普通类型服务类型:[0x00], CIP对象:[0x02 Message Router], ioi segments:[XX]PCCC(带cmd和func)服务类型:[0x00], CIP对象:[0x02 Message Router], cmd:[0x101], fnc:[0x101]..._cip协议embedded_service_error
文章浏览阅读2.4k次,点赞9次,收藏13次。有时候我们在MFC项目开发过程中,需要用到一些微软已经提供的功能,如VC++使用EXCEL功能,这时候我们就能直接通过VS2019到如EXCEL.EXE方式,生成对应的OLE头文件,然后直接使用功能,那么,我们上篇文章中介绍了vs2017及以前的版本如何来添加。但由于微软某些方面考虑,这种方式已被放弃。从上图中可以看出,这一功能,在从vs2017版本15.9开始,后续版本已经删除了此功能。那么我们如果仍需要此功能,我们如何在新版本中添加呢。_vs添加mfc库
文章浏览阅读785次。用ac3编码,执行编码函数时报错入如下:[ac3 @ 0x7fed7800f200] frame_size (1536) was not respected for anon-last frame (avcodec_encode_audio2)用ac3编码时每次送入编码器的音频采样数应该是1536个采样,不然就会报上述错误。这个数字并非刻意固定,而是跟ac3内部的编码算法原理相关。全网找不到,国内音视频之路还有很长的路,音视频人一起加油吧~......_frame_size (1024) was not respected for a non-last frame
文章浏览阅读230次,点赞2次,收藏2次。创建Android应用程序一个项目里面可以有很多模块,而每一个模块就对应了一个应用程序。项目结构介绍_在安卓移动应用开发中要在活动类文件中声迷你一个复选框变量