剑客
关注科技互联网

[原]C++11模版新特性

模板友元

C++11新标准中,可以声明一个类的模板参数类型为类的友元。

template<typename T> class Bar
{
friend T;
protected:
    int val = 100;
};

class Foo
{
public:
    void print_bar(Bar<Foo> &bar) {std::cout<<"bar:/t"<<bar.val<<std::endl;}
};

std::cout<<"test friend template type:/n";
Bar<Foo> bar;
Foo foo;
foo.print_bar(bar);
std::cout<<"test friend template type done./n"<<std::endl;

这比以前的标准中对模版友元的支持更深入了。见博文总结: http://blog.csdn.net/u010487568/article/details/50478843

类型模板别名

使用using语法声明新的类型别名时,也可以带上模版参数。

template<typename T> using twin = std::pair<T, T>;
template<typename T> using str_int = std::pair<T, int>;

std::cout<<"test template alias:/n";
twin<std::string> twin_str = {"abc", "def"};
std::cout<<"twin_str:/t"<<twin_str.first<<'/t'<<twin_str.second<<std::endl;
str_int<std::string> strno = {"abc", 100};
std::cout<<"strno:/t"<<strno.first<<'/t'<<strno.second<<std::endl;
std::cout<<"test template alias done./n"<<std::endl;

在声明str_int的时候,还可以用某一类型部分实例化模板。这是一个挺好用的特性。

模版默认参数

模版参数的默认参数虽然以前都支持,但是新标准中可以使用其他模版参数给出默认参数:

template<typename T, typename F=std::less<T>>
int compare(const T &v1, const T &v2, F f=F())
{
    if(f(v1, v2)) return -1;
    if(f(v2, v1)) return 1;
    return 0;
}

std::cout<<"test default template parameter:/n";
std::cout<<"compare int 1 2:/t"<<compare(1, 2)<<std::endl;
std::cout<<"compare int 2.0 1.0:/t"<<compare(2.0, 1.0)<<std::endl;
//std::cout<<"compare int 2.0 1:/t"<<compare(2.0, 1)<<std::endl; // wrong. can't determine which type is T
std::cout<<"test default template parameter done./n"<<std::endl;

模版参数 F
使用了同时也为模版参数 T
的默认参数 std::less<T>
,这有些类似于模版偏特化的形式,相当于默认参数依赖与前一个模版参数。

尾置返回类型

当时用模板定义一个函数时,有时函数的返回类型是和模板参数相关的,这在以前的标准中可以使用 显示实参
的方式声明多个模版参数进行:

temp1ate <c1ass T1 , c1ass T2 , c1ass T3>
Tl sum(T2 , T3);

//使用显示模版实参列表语法,编译器从左到右依次匹配,最后顺序匹配实参的类型
long va13 = sum<long, int, long> (1, 10L);

新标准可以通过decltype获得返回类型:

std::vector<int> numbers = {1, 2, 3, 4, 5};
auto iter = numbers.begin();

template<typename It>
auto get_begin(It beg) -> decltype(*beg)
{
    return *beg;
}
auto a = get_begin(iter);

//旧式解决办法使用多声明一个模版参数解决
template<typename T, typename It>
T get_begin(It beg)
{
    return *beg;
}
int a = get_begin<*iter, iter>(iter);

上述两种方式对比很明显,可以避免多使用一个模版参数。调用起来也更方便和简介。

可变参数模板

新标准中,可以定义一个不定长度的模板参数列表。

template<typename T>
std::ostream &print_variadic(std::ostream &os, const T &t)
{
    return os<<t<<std::endl;
}

template<typename T, typename... Args>
std::ostream &print_variadic(std::ostream &os, const T &t, const Args&... rest)
{
    os<<t<<"(remain size: "<<sizeof...(Args)<<"), ";
    return print_variadic(os, rest...);
}

std::cout<<"test variadic template:/n";
print_variadic(std::cout, 100, "s", 56.76, 101);
std::cout<<"test variadic template done./n";

第一次为 print_variadic
传入了4个需要打印的对象,则实例化第二个不定长模板函数,将100赋值给 t
,并将剩余的3个参数打包成 rest

在内部递归中,将不断的将 rest
包中的第一个参数拿出来付给 t
,剩余参数打包进行下一次递归调用。

最后只剩一个参数时,两种形式的 print_variadic
都可以匹配,但是第一种没有模版参数包的版本更加特例化,因此将调用第一种形式的 print_variadic
,结束递归。

还有可以定义如下的方式来构造可变参数的对象:

template<typename T>
class A {
public:
    template<typename... Args>
    A(Args...args){
        _member = new T(args...);
    }

//...

private:
    T *_member;
};

上述的构造函数可以依据 T
类型构造时所需的不同个数的参数,使用 new T(args...)
的方式进行通用的构造。

暂总结于此。

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址