0
点赞
收藏
分享

微信扫一扫

拿捏c语言指针(上)

        

目录

一、C语言版本

 二、C++的实现方法

2.1数据包

2.2sizeof...运算符

2.3可变参数模板的使用

2.4emplace_back()


        有时候我们在编写函数时,可能不知道要传入的参数个数,类型 。比如我们要实现一个叠加函数,再比如c语言中的printf,c++中的emplace_last()。

那么这些函数是如何实现的呢?

一、C语言版本

在 C 中,可变参数通过 <stdarg.h> 头文件中的宏来处理。最常用的宏是 va_list、va_start、va_arg 和 va_end。以下是这些宏的简要说明:

 下面我们将结合一段代码来简单的讲解

#include<iostream>
#include<stdarg.h>
int  addsum(int num, ...)
{
    va_list args;
    va_start(args, num);
    int ret=0;
    for (int i = 0; i < num; i++)
    {
        int temp = va_arg(args, int);
        ret+=temp;
    }
    va_end(args);
    return ret;
}

int main()
{
    std::cout<<addsum(5, 1, 2, 3, 4, 5);
    
    return 0;
}

在 C 语言中,如果你使用了 va_start 宏来初始化可变参数列表,那么你至少需要传递一个参数作为固定参数,以便确定可变参数列表的起始位置。这个固定参数通常被称为 "sentinel" 或 "sentinel value"。

那么这个"sentinel" 或 "sentinel value"。一定要是参数个数吗?

 二、C++的实现方法

2.1数据包

在 C++ 中,也可以使用可变参数模板来实现类似的功能,这种技术更加灵活,并且不需要使用宏。C++11 引入了新的语法和标准库支持,使得可变参数模板更加易用和安全。

c++在c++11中提出了可变参数模板的概念,所谓可变参数模板就是一个接受可变数目参数模板的函数或模板类。可变数目的参数被称作参数包。存在两种参数包:

我们使用“...”来表示一个包,在一个模板参数列表中,class..或typname...表示接下来 的参数表示零个或多个类型的列表;一个类型名后面跟一个省略号表示零个或多个给定类型的非类型参数的列表。在函数参数列表中,如果一个参数的类型是一个模板参数包,则此参数也是一个函数参数包。例如:

//Args是一个模板参数包;rest是一个函数参数包
//Args表示零个或多个模板类型参数//rest表示零个或多个函数参数
template <typename T,typename...Args>
void foo(const T 6t,const Args6 ..  rest);

声明了foo是一个可变参数函数模板,它有一个名为T的类型参数,和一个名为Args的模板参数包。这个包表示零个或多个额外的类型参数。foo的函数参数列表包含一个const s类型的参数,指向T的类型,还包含一个名为rest的函数参数包,此包表示零个或多个函数参数。

与往常一样,编译器从函数的实参推断模板参数类型。对于一个可变参数模板,编译器还会推断包中参数的数目。例如,给定下面的调用

int i= 0;
double d=3.14;
string s="how now brown cow";
foo(i,s,42,d); //包中有三个参数
fog(s.42,"hi");//包中有两个参数
foo(d,s);      //包中有一个参数
foo("hi");      //空包

编译器会为foo实例化出四个不同的版本:

void foo(const int&,const string&,const int&,const double&);
void foo(const string&,const int&,const char[3]&);
void foo(const double&,const string&);
void foo(const char[3]&);

在每个实例中,T的类型都是从第一个实参的类型推断出来的。剩下的实参(如果有的话)提供函数额外实参的数目和类型。

2.2sizeof...运算符

当我们需要知道包中有多少元素时,可以使用sizeof...运算符。类似sizeof返回一个常量表达式

template<typename ...Args>
void g(Args .args)
{
    cout <<sizeof...(Args)<<end1;//类型参数的数目
    cout <<sizeof...(args)<<endl;//函数参数的数目
}

2.3可变参数模板的使用

void _ShowList()
{
    // 结束条件的函数
    std::cout << std::endl;
}

template <class T, class... Args>
void _ShowList(T val, Args... args)
{
    std::cout << val << " ";
    _ShowList(args...);
}

// args代表0-N的参数包
template <class... Args>
void CppPrint(Args... args)
{
    _ShowList(args...);
}
int main()
{
    CppPrint(1, 2, 2.2, string("xxxx"));
}

一般来说我们是使用递归的方式来将参数全部使用,当函数全部使用后就会匹配到结束函数。

template <class T>
void PrintArg(T t)
{
    std::cout << t << " ";
}
// args表示0-N的参数包
template <class... Args>
void CppPrintf(Args... args)
{
    int a[] = {0, (PrintArg(args), 0)...};
    cout << endl;
}

c++在编译时要确定数组a的大小来给空间,所以他会将里面的那个数据包展开,如图()中是一个逗号表达式,也就是有几个参数就会调用几下PrintArg。

2.4emplace_back()

emplace_back 是 C++ 中标准库容器 std::vector 的一个成员函数,用于在容器的尾部直接构造一个新元素,而不是先创建一个临时对象再拷贝或移动到容器中.

使用 emplace_back 可以直接在容器的尾部构造一个新元素,而不需要手动创建该元素的实例。emplace_back 接受任意数量的参数,这些参数会被传递给元素类型的构造函数,用于直接在容器中构造新元素。

所以网上有人说emplace_back代价更小,但是事实上移动拷贝代价更小,所以这句话应该有前提就是当元素类型是不可拷贝的时候。

举报

相关推荐

0 条评论