posted @ 2011-10-09 20:00 feng wang 阅读(786) 评论(2) 编辑
Variadic Templates 的引入,消去了烦冗的模板特化。 一个例子:
#include <iostream>
double do_sum()
{
return 0;
}
template< typename T, typename... Args >
double do_sum( T&& t, Args&& ... args )
{
return t + do_sum( args... );
}
int main()
{
std::cout << do_sum( 1.0, 2.0, 3.0, 4.0 )
<< std::endl;
return 0;
}
这里需要注意的有两点:
- double do_sum() 这个函数必须在变长模板函数 double do_sum( T&& t, Args&& ... args ) 之前声明
- 变长模板函数实现中必须使用另外一个函数递归实现
- 模板上用的是 template< typename... Args>
- 函数参数中用的是 double do_sum(Arg ... arg)
- 函数体中用的是 do_sum(arg...)
大致可以看出,有 typename 的时候 .. 跟 typename 后边,否则跟在 Arg 后边,最后则是在参数 arg 后边
如果需要知道到底传入了多少个参数可以这样来
template<typename ... Args>
std::size_t how_many_args(Args ... args)
{
return sizeof...(args);
}
variadic template 基本使用到这里就差不多了,下边的内容略略而过即可
再次注意这个 ...,来个稍微有点复杂的
template<typename... T>
void f(T (* ...t)(int, int));
int add(int, int);
float subtract(int, int);
void g()
{
f(add, subtract);
}
再来一个多继承
template<typename... Mixins>
class X : public Mixins...
{
public:
X(const Mixins&... mixins)
: Mixins(mixins)... { }
};
整数也是可以放在 variadic template 上的
template<class... Types> class Tuple; // Types is a template type parameter pack template<class T, int... Dims> struct multi array; // Dims is a non-type template parameter pack缺省template参数也是可以的,比如
template<class T = char> class String; String<>* p; // OK: String<char> String* q; // syntax error template<typename ... Elements> class Tuple; Tuple<>* t; // OK: Elements is empty Tuple* u; // syntax error还有 template template 的这种
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template<class... Types> class C { /* ... */ };
template<template<class> class P> class X { /* ... */ };
template<template<class...> class Q> class Y { /* ... */ };
X<A> xa; // okay
X<B> xb; // ill-formed: default arguments for the parameters of a template template argument are ignored
X<C> xc; // ill-formed: a template parameter pack does not match a template parameter
Y<A> ya; // ill-formed: a template parameter pack does not match a template parameter
Y<B> yb; // ill-formed: a template parameter pack does not match a template parameter
Y<C> yc; // okay
也特别注意 Y<B> 和 X<B> 无论哪个都不能通过。这是因为在 template 中,模板类型顺序是不可以搞错的,比如:
template<class T1, class T2>
struct A {
void f1();
void f2();
};
template<class T2, class T1>
void A<T2,T1>::f1() { } // OK
template<class T2, class T1>
void A<T1,T2>::f2() { } // erro
于是同样就有
template<class... Types> struct B {
void f3();
void f4();
};
template<class... Types>
void B<Types...>::f3() { } // OK
更详细的有
template<class X, class Y> X f(Y);
template<class X, class Y, class... Z> X g(Y);
int i = f<int>(5.6); // Y is deduced to be double
int j = f(5.6); // ill-formed: X cannot be deduced
f<void>(f<int, bool>); // Y for outer f deduced to be
// int (*)(bool)
f<void>(f<int>); // ill-formed: f<int> does not denote a
// single function template specialization
int k = g<int>(5.6); // Y is deduced to be double, Z is deduced to an empty sequence
f<void>(g<int, bool>); // Y for outer f deduced to be
// int (*)(bool), Z is deduced to an empty sequence
注意这种特化
template<typename...>
struct Tuple { };
template<typename... Types>
void g(Tuple<Types...>); // #1
template<typename T1, typename... Types>
void g(Tuple<T1, Types...>); // #2
template<typename T1, typename... Types>
void g(Tuple<T1, Types&...>); // #3
g(Tuple<>()); // calls #1
g(Tuple<int, float>()); // calls #2
g(Tuple<int, float&>()); // calls #3
g(Tuple<int>()); // calls #3
还有多个 variadic template 嵌套着用的
template<typename...>
struct Tuple {};
template<typename T1, typename T2>
struct Pair {};
template<typename... Args1>
struct zip {
template<typename... Args2>
struct with {
typedef Tuple<Pair<Args1, Args2>...> type;
};
};
typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
// T1 is Tuple<Pair<short, unsigned short>, Pair<int, unsigned> >
typedef zip<short>::with<unsigned short, unsigned>::type T2; // error: different number of arguments specified
// for Args1 and Args2
template<typename... Args>
void g(Args... args)
{
f(const cast<const Args*>(&args)...);
// okay: ‘‘Args’’ and ‘‘args’’ are expanded
f(5 ...); // error: pattern does not contain any parameter packs
f(args); // error: parameter pack ”args” is not expanded
f(h(args...) + args...);
// okay: first ‘‘args’’ expanded within h, second ‘‘args’’ expanded within f.
}
再举一个完整的例子:
template<typename T>
void print_comma_separated_list(T&& value)
{
std::cout<<value<<std::endl;
}
template<typename First,typename ... Rest>
void print_comma_separated_list(First&& first,Rest&& ... rest)
{
std::cout<<first<<",";
print_comma_separated_list(rest...);
}
int main()
{
print_comma_separated_list(42,"hello",2.3,'a');
print_comma_separated_list("hello",2.3,'a');
print_comma_separated_list(2.3,'a');
print_comma_separated_list('a');
return 0;
}
特别说明:
本文例程多摘自 c++0x N2242,这里可以找到:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdfposted @ 2011-05-04 23:28 feng wang 阅读(1079) 评论(1) 编辑
引子
雨晨跟小艾拍拖都快两年了,终于到了要见家长的关头;雨晨相当紧张,因为第一印象相当重要,尤其体现在穿着打扮上,毕竟“人靠衣装,佛靠金装”么。小艾非常体谅雨晨的心情,于是拉他到一家男装超市(就当真的有这种超市吧),血拼开始:)
深谙自家父母的品位的小艾提出了这样的选购方案:
1) 要求商家将所有的上衣编号为1, 2, 3, ... , m;所有类别的裤子编号为 1, 2, ..., n;所有类别的鞋子标号为1, 2, ..., o; (---- 假设有这样耐心的商家)
2) 要求雨晨依次从上衣、裤子和鞋子中选取一件,全部穿上后由小艾进行打分 (---- 假设有这样温驯的雨晨)
3) 选购分数最高的组合
雨晨是个程序员,听完这个方案后,出于职业习惯,脑海里很快浮现出这样的伪代码:
代码
1 max_score = 0;
2 choice = choice (0, 0, 0 );
3 for ( size_t i = 0; i < m; ++i )
4 for ( size_t j = 0; j < n; ++j )
5 for ( size_t k = 0; k < o; ++k )
6 {
7 current_score = score( clothes[i], trousers[j], shoes[k] );
8 if ( current_score > max_score )
9 {
10 max_score = current_score;
11 choice = choice( clothes[i], trousers[j], shoes[k] );
12 }
13 }
14 return choice;
两人刚商量好方案,旁听的售货员mm 就热心地推荐小艾在打分的时候考虑一下领带,衬衣, 袜子………… 的搭配,顺便提了一下她们店里的有 N 种男生服装饰品----她也不知道仓库里到底还有多少种 ---- 不过都是已经编号好了随时可以试用的;出于女人的天性,小艾欣然同意。
雨晨,假设雨晨还没有晕倒,还足够理智,脑海还能浮现出伪代码么?
iterator 模式
从程序员的角度来看,上边的问题可以简化为:
已知:
a) N 种男生服饰, N 大小在运行期决定
b) 每种服饰有多个款式, 具体款式数量在运行期决定
需求:
遍历所有服饰,找出所有组合
由于服饰的数量是未知,每种服饰的款式数量也是未知,非常直接的循环嵌套方法在这里很难用得上,需要另谋他路。
假设已经在运行时知道了服饰的数量是N,对应的数量分别是 m_1, m_2, ..., m_N,那么如何做出类似于直接手写的循环遍历呢?
考虑的所有组合的数量是 M = m_1 * m_2 * ... * m_N,如果能够用一种方式,将 数列 1, 2, 3, ..., M 依次映射到各种服饰的对应组合上,问题可迎刃而解。
于是有了 iterator 模式的实作:
代码
1 #include <string>
2 #include <vector>
3 #include <map>
4 #include <cstddef>
5
6 struct finery_chooser
7 {
8 typedef std::string finery_identifier_type; // finery type, such as "clothes", "trousers", etc.
9 typedef std::string finery_style_type; // concret finery style, such as "Nike", "Jordan" ....... in "shoes"
10 typedef finery_style_type value_type;
11 typedef std::vector<value_type> value_arr_type;
12 typedef std::map<finery_style_type, value_arr_type> all_finery_identifer_style_associate_type;
13 typedef std::size_t size_type;
14
15 all_finery_identifer_style_associate_type& afisat;
16
17 struct iterator
18 {
19 typedef iterator self_type;
20 typedef finery_chooser::all_finery_identifer_style_associate_type all_finery_identifer_style_associate_type;
21 typedef finery_chooser::size_type size_type;
22 typedef finery_chooser::value_arr_type value_arr_type;
23 typedef finery_chooser::finery_identifier_type finery_identifier_type;
24 typedef size_type value_type;
25
26 all_finery_identifer_style_associate_type& afisat;
27 size_type n;
28
29 iterator( all_finery_identifer_style_associate_type& afisat_ ) : afisat( afisat_ ), n( 0 ) {}
30 iterator( all_finery_identifer_style_associate_type& afisat_, int ) : afisat( afisat_ ), n( 1 )
31 {
32 for ( auto i = afisat.begin(); i != afisat.end(); ++i )
33 { n *= ( *i ).second.size(); }
34 }
35
36 iterator( const self_type& other ) : afisat( other.afisat ), n( other.n ) {}
37
38 self_type&
39 operator ++ ()
40 {
41 ++n;
42 return *this;
43 }
44
45 const value_arr_type
46 operator * ()
47 {
48 value_type current_n = n;
49 value_arr_type ans;
50
51 for ( auto i = afisat.begin(); i != afisat.end(); ++i )
52 {
53 const finery_identifier_type id = ( *i ).first;
54 const size_type dim = ( *i ).second.size();
55 ans.push_back( ( *i ).second[current_n%dim] );
56 current_n /= dim;
57 }
58
59 return ans;
60 }
61
62 friend bool
63 operator != ( const self_type& lhs, const self_type& rhs )
64 {
65 return lhs.n != rhs.n;
66 }
67 };
68
69 finery_chooser( all_finery_identifer_style_associate_type& afisat_ ) : afisat( afisat_ ) {}
70
71 iterator begin()
72 {
73 return iterator( afisat );
74 }
75
76 iterator end()
77 {
78 return iterator( afisat, size_type() );
79 }
80
81 };
82
不过上边实作代码还缺少了一些标准 stl 迭代器定义的类型,比如pointer, reference, iterator_category 等等,还有 value_type 要修改为 value_arr_type,使用时可以根据需要自行修改添加。
外加一个典型的示例:
代码
1 finery_chooser::all_finery_identifer_style_associate_type afisat;
2
3 //.......................................
4
5 finery_chooser fc( afisat );
6
7 for ( auto i = fc.begin(); i != fc.end(); ++i )
8 {
9 int current_score = score( *i );
10 //............................................
11 }
或者直接:
1 std::for_each( fc.begin(), fc.end(), what_func_you_want);
讨论
通过上边示例代码可以看出,使用 iterator 模式的好处是摆脱了烦冗的循环书写,对于任何习惯 stl 迭代器习惯用法的人来说,代码的表达意思是相当清晰的。
缺点也有,比如实作代码相比传统的嵌套循环表达方式可读性相当差,比如如此抽象成 iterator 模式代码的表达力度并不总是强于传统方式,等等……
实作代码是对引子中的故事量身定制的,这么强的类型耦合其实是没有必要的,范化这段代码的工作就作为练习吧~~~~
多层循环的遍历当然可以有更优雅的表达方式,某已抛砖引玉,静候楼下诸位高论。
posted @ 2010-11-17 01:30 feng wang 阅读(1793) 评论(7) 编辑
编译环境
编译器: g++ 4.5
编译选项: -std=c++0x
链接选项: –pthread
完整编译链接命令: g++ –O2 –o example example.cc -std=c++0x -pthread
头文件:
| 条目 | 头文件 |
| thread | <thread> |
| Mutual exclusion | <mutex> |
| Condition variables | <condition_variable> |
| Futures | <future> |
a 线程创建
I 从函数中创建
如果想在一个线程中执行一个函数 f ,那么这个线程可以这样创建:
std::thread t( f );
如果函数有参数,那么直接把参数列在后边即可:
void hello_from( const char* str const )
{
std::cout << "hello from " << str << "\n";
}
std::thread t( hello_from, "thread t" );
多个参数的函数也是如此:
void max( const long m, const long n )
{
std::cout << "max(" << m << ", " << n << ")=" << (m>n?m:n) << "\n";
}
std::thread t( max, 13, 31 );
可以依此类推到3个、4个……参数的函数情形。
只要不把 main 函数也弄进去,编译器统统接受:
void try_start_program_here()
{
std::thread t( main ); //error
}
II 从对象/仿函数中创建
把仿函数依样搬进去:
struct say_hello
{
void operator()()const
{
std::cout << "Hello.\n";
}
};
std::thread t( say_hello() );
把上边代码敲进去编译一下后,没有发现问题,链接后执行一下,没有看到 Hello. 打印出来, 这是因为编译器把这一行
std::thread t( say_hello() );
当成一个函数声明了(这个函数返回一个 std::thread 类型对象, 接受一个缺省的函数指针,这个函数指针返回一个 say_hello 类型对象,并且无参数)。
这是一个老问题了,从标准输入读取字符填充到一个 vector 的时候也曾有过这样的不便:
std::vector<std::string> vs( (std::istream_iterator<std::string>(cin)), std::istream_iterator<std::string>() );//parsed as an object std::vector<std::string> vs( std::istream_iterator<std::string>(cin), std::istream_iterator<std::string>() ); //parsed as a function
依样画葫芦,再套上一层括号搞定:
struct say_hello
{
void operator()()const
{
std::cout << "Hello.\n";
}
};
std::thread t( (say_hello()) );
或者用时髦一点的 c++0x 引入的初始化语法:
struct say_hello
{
void operator()()const
{
std::cout << "Hello.\n";
}
};
std::thread t{ say_hello() };
直接复制一个对象也成:
struct say_hello
{
void operator()()const
{
std::cout << "Hello.\n";
}
};
say_hello sh;
std::thread t( sh );
如果想消去对象复制的成本,那就传 reference :
struct say_hello
{
void operator()()const
{
std::cout << "Hello.\n";
}
};
say_hello sh;
std::thread t( std::ref(sh) );
这个 std::ref 也是 c++0x 引入的,需要多包含一个头文件:
#include <functional>
照着函数的处理方法,带参数的仿函数也这么干:
struct
hello_from
{
void operator()()const
{
std::cout << "hello from default\n";
}
void operator()( const char* const name )
{
std::cout << "hello from " << name << "\n";
}
};
std::thread t1{ hello_from() };
std::thread t2{ hello_from(), "http://www.cnblogs.com/feng_wang/" };
多个参数的仿函数情形依此类推。
需要参数进行初始化的对象也是如此:
struct say_something
{
private:
std::string name_;
public:
say_something( const std::string& name ) :
name(name_) {}
void operator()(const std::string& what) const
{
std::cout << name << " said \"" << what << "\"\n";
}
};
std::thread t( say_something( "feng" ), "welcome." );
调用对象中的方法的时候则需要提供对象本身:
struct say_hello
{
void say( const std::string& what ) const
{
std::cout << what << "\n";
}
};
say_hello s;
std::thread t( &say_hello::say, &s, "hello" );
提供一个指针也可以:
struct say_hello
{
void say( const std::string& what ) const
{
std::cout << what << "\n";
}
};
say_hello* s = new say_hello;
std::thread t( &say_hello::say, s, "hello" );
不过为了防止这个线程执行过程中所传入的指针被另外一个线程意外释放,最好用智能指针 shared_ptr
struct say_hello
{
void say( const std::string& what ) const
{
std::cout << what << "\n";
}
};
std::shared_ptr<say_hello> s ( new say_hello );
std::thread t( &say_hello::say, s, "hello" );
shared_ptr 也是 c++0x 提供的,为了使用它,需要
#include <memory>
当然,如果线程调用对象中的一个 static 方法就不需要提供对象本身了(注意 static 的方法不能有 c-v 限定):
struct say_hello
{
static void say( const std::string& what )
{
std::cout << what << "\n";
}
};
std::thread t( &say_hello::say, "hello" );
b 等待线程完结或者取消等待
这个很简单,单线程的时候只有 main 这一个线程,现在有了多线程之后,可以在创建新线程的线程中选择是否等待本线程创建的线程执行结束,还是直接返回:
等待完成:
//thread A void f(); std::thread t( f ); t.join();
直接结束:
void f(); std::thread t( f ); t.detach();
上边说了这么多,给出一个完整的例子结束本篇:
#include <iostream>
#include <thread>
void thanks()
{
std::cout << "Thank you for your reading.\n";
};
int main()
{
std::thread t( thanks );
t.join();
return 0;
}posted @ 2010-09-21 17:42 feng wang 阅读(2067) 评论(9) 编辑

