- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章一篇文章让你彻底明白c++11增加的变参数模板由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
本篇文章介绍一下c++11中增加的变参数模板template<typename... _Args>到底是咋回事,以及它的具体用法.
说明一下,我用的是gcc7.1.0编译器,标准库源代码也是这个版本的.
按照惯例,还是先看一下本文大纲,如下:
在之前写vector和deque容器源码剖析的过程中,经常发现这样的代码,如下:
1
2
|
template
<
typename
... _Args>
void
emplace_front(_Args&&... __args);
|
可以看到里面模板参数是template<typename... _Args>,其实这个就是变参数模板,然后它的参数也是比较特别的_Args&&... __args,去除右值引用的话,它就是一个可变参数,那么可变参数模板和可变参数到底是什么,应该怎么使用呢,我们今天就来深究一下这些事情.
c++11中新增加了一项内容,叫做变参数模板,所谓变参数模板,顾名思义就是参数个数和类型都可能发生变化的模板,要实现这一点,那就必须要使用模板形参包.
模板形参包是可以接受0个或者n个模板实参的模板形参,至少有一个模板形参包的模板就可以称作变参数模板,所以说白了,搞懂了模板形参包就明白变参数模板了,因为变参数模板就是基于模板形参包来实现的,接下来我们就来看看到底啥是模板形参包.
模板形参包主要出现在函数模板和类模板中,目前来讲,模板形参包主要有三种,即:非类型模板形参包、类型模板形参包、模板模板形参包.
非类型模板形参包语法是这样的:
1
|
template
<类型 ... args>
|
初看会很疑惑,说是非类型模板形参包,怎么语法里面一开始就是一个类型的,其实这里的非类型是针对typename和class关键字来的,都知道模板使用typename或者class关键字表示它们后面跟着的名称是类型名称,而这里的形参包里面类型其实表示一个固定的类型,所以这里其实不如叫做固定类型模板形参包.
对于上述非类型模板形参包而言,类型选择一个固定的类型,args其实是一个可修改的参数名,如下:
1
|
template
<
int
... data> xxxxxx;
|
注意,这个固定的类型是有限制的,标准c++规定,只能为整型、指针和引用.
但是这个形参包该怎么用呢,有这样一个例子,比如我想统计这个幼儿园的小朋友们的年龄总和,但是目前并不知道总共有多少个小朋友,那么此时就可以用这个非类型模板形参包,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <iostream>
using
namespace
std;
//这里加一个空模板函数是为了编译可以通过,否则编译期间调用printAmt<int>(int&)就会找不到可匹配的函数
//模板参数第一个类型实际上是用不到的,但是这里必须要加上,否则就是调用printAmt<>(int&),模板实参为空,但是模板形参列表是不能为空的
template
<
class
type>
void
printAmt(
int
&iSumAge)
{
return
;
}
template
<
class
type,
int
age0,
int
... age>
void
printAmt(
int
&iSumAge)
{
iSumAge += age0;
//这里sizeof ... (age)是计算形参包里的形参个数,返回类型是std::size_t,后续同理
if
( (
sizeof
... (age)) > 0 )
{
//这里的age...其实就是语法中的一种包展开,这个后续会具体说明
printAmt<type, age...>(iSumAge);
}
}
int
main()
{
int
sumAge = 0;
printAmt<
int
,1,2,3,4,5,7,6,8>(sumAge);
cout <<
"the sum of age is "
<< sumAge << endl;
return
0;
}
|
这里只是以此为例来说明一下非类型模板形参包的使用,实际项目中这么简单的事肯定是没有必要还写个模板的.
根据语法和代码的使用情况,我们对非类型模板形参包总结如下:
类型模板形参包语法如下:
typename|class ... Args 。
这个就是很正常的模板形参了哈,typename关键字和class关键字都可以用于在模板中声明一个未知类型,只是在以前template<typename type>的基础上加了一个省略号,改成了可变形参包而已,该可变形参包可以接受无限个不同的实参类型.
现在我们先用一下这个类型模板形参包看看,假设我们有这样一种场景,我想输出一个人的姓名、性别、年龄、身高等个人信息,但是具体有哪些信息我们不能确定,那应该怎么办呢?
分析一下,具体信息不固定,类型也不固定,此时就可以使用类型模板形参包了,看下面这段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include <iostream>
using
std::cout;
using
std::endl;
void
xprintf()
{
cout << endl;
}
template
<
typename
T,
typename
... Targs>
void
xprintf(T value, Targs... Fargs)
{
cout << value <<
' '
;
if
( (
sizeof
...(Fargs)) > 0 )
{
//这里调用的时候没有显式指定模板,是因为函数模板可以根据函数参数自动推导
xprintf(Fargs...);
}
else
{
xprintf();
}
}
int
main()
{
xprintf(
"小明个人信息:"
,
"小明"
,
"男"
, 35,
"程序员"
, 169.5);
return
0;
}
|
输出结果如下:
小明个人信息: 小明 男 35 程序员 169.5 。
这个就是一个类型模板形参包在函数模板里面的典型使用,可以看到, 。
当然啦,有人会说了,其实cout一行代码就可以搞定,但是我们这里是提供通用型接口,具体要输出哪些信息事先并不知道,这个时候使用类型模板形参包就很方便啦.
这个就有点绕了,模板模板形参包,有点不好理解,还是先看一下语法看看:
template < 形参列表 > class ... Args(可选) 。
其实说白了,就是说这个形参包本身它也是一个模板,在看模板模板形参包之前,我们先介绍一下模板模板形参,因为形参包说白了,就是在形参的基础上增加了省略号实现的.
我们先看一下标准库中对模板模板形参的使用,找到头文件bits/alloc_traits.h,在模板类allocator_traits的声明中有这样一个结构体,如下:
1
2
3
4
5
|
template
<
template
<
typename
>
class
_Func,
typename
_Tp>
struct
_Ptr<_Func, _Tp, __void_t<_Func<_Alloc>>>
{
using
type = _Func<_Alloc>;
};
|
这里的意思就是说_Func这个模板形参本身是一个带模板的类型,使用的时候是需要声明模板实参的.
假设有这样一种场景,我们需要定义一个vector变量,但不能确定vector的元素类型,此时该怎么办呢?
看如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#include <typeinfo>
#include <cxxabi.h>
#include <iostream>
#include <vector>
//将gcc编译出来的类型翻译为真实的类型
const
char
* GetRealType(
const
char
* p_szSingleType)
{
const
char
* szRealType = abi::__cxa_demangle(p_szSingleType, nullptr, nullptr, nullptr);
return
szRealType;
}
//这里的func是一个模板模板形参
template
<
template
<
typename
,
typename
>
class
func,
typename
tp,
typename
alloc = std::allocator<tp> >
struct
temp_traits
{
using
type = func<tp, alloc>;
type tt;
//根据模板类型定义一个成员变量
};
int
main()
{
temp_traits<std::vector,
int
> _traits;
//获取结构体字段tt的类型
const
std::type_info &info =
typeid
(_traits.tt);
std::cout << GetRealType(info.name()) << std::endl;
return
0;
}
|
输出结果如下:
std::vector<int, std::allocator<int> > 。
这里类型temp_tratis里面根据模板模板形参和其他模板形参来实现了我们的使用场景.
理解了模板模板形参,再来看看模板模板形参包的使用,这个与类型模板形参包没什么两样,只不过类型换成了一个带模板的类型而已,看下面这段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
#include <typeinfo>
#include <cxxabi.h>
#include <iostream>
#include <vector>
#include <deque>
#include <list>
//将gcc编译出来的类型翻译为真实的类型
const
char
* GetRealType(
const
char
* p_szSingleType)
{
const
char
* szRealType = abi::__cxa_demangle(p_szSingleType, nullptr, nullptr, nullptr);
return
szRealType;
}
//泛化变参模板
template
<
typename
tp,
typename
alloc,
template
<
typename
,
typename
>
class
... types >
struct
temp_traits
{
temp_traits(tp _tp)
{
std::cout <<
"泛化模板执行"
<< std::endl;
}
};
//偏特化变参模板
template
<
typename
tp,
typename
alloc,
template
<
typename
,
typename
>
class
type,
template
<
typename
,
typename
>
class
... types >
struct
temp_traits<tp, alloc,type, types...>:
public
temp_traits<tp, alloc, types...>
{
using
end_type = type<tp, alloc>;
end_type m_object;
temp_traits(tp _tp)
:temp_traits<tp, alloc, types...>(_tp)
{
const
std::type_info &info =
typeid
(m_object);
std::cout <<
"偏特化版本执行, 此时类型:"
<< GetRealType(info.name()) << std::endl;
m_object.push_back(_tp);
}
void
print()
{
auto it = m_object.begin();
for
(;it != m_object.end(); ++it)
{
std::cout <<
"类型为:"
<< GetRealType(
typeid
(end_type).name()) <<
", 数据为:"
<< *it << std::endl;
}
}
};
int
main()
{
temp_traits<
int
, std::allocator<
int
>, std::vector, std::deque, std::list> _traits(100);
_traits.print();
return
0;
}
|
这段代码就相当不好理解了,我们可以认为它是一个递归继承的过程,但到底是怎么个递归继承法呢?可以先看一下执行结果,由结果来倒推递归过程.
先看一下执行结果,如下:
泛化模板执行 偏特化版本执行, 此时类型:std::__cxx11::list<int, std::allocator<int> > 偏特化版本执行, 此时类型:std::deque<int, std::allocator<int> > 偏特化版本执行, 此时类型:std::vector<int, std::allocator<int> > 类型为:std::vector<int, std::allocator<int> >, 数据为:100 。
根据4次构造函数的调用,我们可以得出结论:形参包包含多少个形参,它就会在此基础上有几层继承,所以现在是3个形参,3层继承,顶层基类是泛化模板,然后进行了三层派生,这个递归继承的过程是编译器根据代码自行展开的.
再看看对于成员函数print的调用,我的原意是想针对每一种容器类型,都打印出结果,但现在只打印了一种,我们可以想想,对于继承,非虚函数但函数类型相同的情况下,派生类的成员函数会覆盖基类的成员函数,所以这里结果是正常的.
那么怎么实现我们要的效果呢,答案是使用析构函数,层层析构,所以将成员函数print函数修改为如下代码:
1
2
3
4
5
6
7
8
|
~temp_traits()
{
auto it = m_object.begin();
for
(;it != m_object.end(); ++it)
{
std::cout <<
"类型为:"
<< GetRealType(
typeid
(end_type).name()) <<
", 数据为:"
<< *it << std::endl;
}
}
|
此时输出结果如下:
泛化模板执行 偏特化版本执行, 此时类型:std::__cxx11::list<int, std::allocator<int> > 偏特化版本执行, 此时类型:std::deque<int, std::allocator<int> > 偏特化版本执行, 此时类型:std::vector<int, std::allocator<int> > 类型为:std::vector<int, std::allocator<int> >, 数据为:100 类型为:std::deque<int, std::allocator<int> >, 数据为:100 类型为:std::__cxx11::list<int, std::allocator<int> >, 数据为:100 。
到这里,我们对模板模板形参包应该就有了比较深的了解了.
注意,不论是哪种形参包,形参包都需要放在模板的最后面,否则编译就会有问题.
我们都知道函数形参是什么,那么函数形参包呢,它到底是什么,先看看函数形参包的语法:
Args ... args 。
这里的Args...代表形参包类型,这个类型就是模板形参包里面声明的类型,args就是函数的形参名称了,是可以自定义的.
那么是所有的模板形参包声明类型都可以作为函数形参包类型吗,不是的,前面我们讲了三种模板形参包,这其中除了非类型的模板形参包因为类型固定且是具体的值,不能作为函数形参包以外,类型模板形参包和模板模板形参包因为声明的都是类型,所以他们是可以用作函数形参的类型的.
类型模板形参包声明函数形参我们在2.2节的代码举例里面已经说明了,这里不再举例,我们看下模板模板行参包怎么样作为函数的形参,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
#include <typeinfo>
#include <cxxabi.h>
#include <iostream>
#include <vector>
#include <list>
#include <deque>
//将gcc编译出来的类型翻译为真实的类型
const
char
* GetRealType(
const
char
* p_szSingleType)
{
const
char
* szRealType = abi::__cxa_demangle(p_szSingleType, nullptr, nullptr, nullptr);
return
szRealType;
}
void
xprintf()
{
std::cout <<
"调用空函数"
<< std::endl;
}
template
<
typename
tp,
typename
alloc,
template
<
typename
,
typename
>
class
T,
template
<
typename
,
typename
>
class
... Targs >
void
xprintf(T<tp, alloc> value, Targs<tp, alloc>... Fargs)
{
std::cout <<
"容器类型:"
<< GetRealType(
typeid
(value).name()) << std::endl;
std::cout <<
"容器数据:"
<< std::endl;
auto it = value.begin();
for
(; it != value.end(); ++it)
{
std::cout << *it <<
','
;
}
std::cout << std::endl;
if
( (
sizeof
...(Fargs)) > 0 )
{
//这里调用的时候没有显式指定模板,是因为函数模板可以根据函数参数自动推导
xprintf(Fargs...);
}
else
{
xprintf();
}
}
int
main()
{
std::vector<
int
> vt;
std::deque<
int
> dq;
std::list<
int
> ls;
for
(
int
i =0 ; i < 10 ; ++i)
{
vt.push_back(i);
dq.push_back(i);
ls.push_back(i);
}
xprintf(vt, dq, ls);
return
0;
}
|
这个就是一个典型的使用模板模板形参包类型作为函数形参的案例,说白了,我们要理解函数形参包的本质,它其实还是一个函数形参,既然是函数形参,就脱离不了类型加参数名的语法,形参包无非就是在类型后面加个省略号,而模板模板形参包作为函数形参类型的时候一定要记得加模板参数,比如代码里面T<tp, alloc>这样才是一个完整的类型,光是一个T,它的类型就是不完整的.
理解了以上的这一点,我们对函数形参包的使用就没有难度了.
到底啥是形参包展开,我们先看看语法,如下:
模式 ... 。
在模式后面加省略号,就是包展开了,而所谓的模式一般都是形参包名称或者形参包的引用,包展开以后就变成零个或者多个逗号分隔的实参.
比如上面的age ...和Fargs...都属于包展开,但是要知道,这种形式我们是没有办法直接使用的,那么具体该怎么使用呢,有两种办法:
递归方法适用场景:多个不同类型和数量的参数有比较相似的动作的时候,比较适合使用递归的办法.
关于递归办法的使用,前面几节有多个案例了,这里不再展开多说.
关于整个形参包传递的使用方法,看下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
#include <iostream>
#include <string>
using
namespace
std;
class
programmer
{
string name;
string sex;
int
age;
string vocation;
//职业
double
height;
public
:
programmer(string name, string sex,
int
age, string vocation,
double
height)
:name(name), sex(sex), age(age), vocation(vocation), height(height)
{
cout <<
"call programmer"
<< endl;
}
void
print()
{
cout <<
"name:"
<< name << endl;
cout <<
"sex:"
<< sex << endl;
cout <<
"age:"
<< age << endl;
cout <<
"vocation:"
<< vocation << endl;
cout <<
"height:"
<< height << endl;
}
};
template
<
typename
T>
class
xprintf
{
T * t;
public
:
xprintf()
:t(nullptr)
{}
template
<
typename
... Args>
void
alloc(Args ... args)
{
t =
new
T(args...);
}
void
print()
{
t->print();
}
void
afree()
{
if
( t != nullptr )
{
delete
t;
t = nullptr;
}
}
};
int
main()
{
xprintf<programmer> xp;
xp.alloc(
"小明"
,
"男"
, 35,
"程序员"
, 169.5);
xp.print();
xp.afree();
return
0;
}
|
这里类型xprintf是一个通用接口,类模板中类型T是一个未知类型,我们不知道它的构造需要哪些类型、多少个参数,所以这里就可以在它的成员函数中使用变参数模板,来直接把整个形参包传递给构造函数,具体需要哪些实参就根据模板类型T的实参类型来决定.
再来说回一开始的案例,如下:
1
2
|
template
<
typename
... _Args>
void
emplace_front(_Args&&... __args);
|
这个是deque容器里面的函数,函数emplace_front可以说是push_front的一个优化版本,从它的原型可以看出,这个函数就是类型模板形参包的典型使用,只不过这里多了两个符号&&,这个我们先前也讲过,它代表右值引用,对于右值引用,如果元素类型是int、double这样的原生类型,其实右值引用和直接传值,区别不是很大.
那么这里函数原型中的参数_Args&&... __args到底代表什么呢,抛开右值引用不说,它就是多个参数,难道是可以在容器中插入多个不同类型的元素吗,并不是啊,容器中的元素是必须要一致的,这里的参数其实是容器定义时元素类型构造函数的多个参数,也就是说,函数emplace_front可以直接传入元素的构造参数,下面我们看看到底是怎么使用的,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <deque>
#include <string>
#include <iostream>
class
CMan
{
int
age;
std::string sex;
double
money;
public
:
CMan(
int
age, std::string sex,
double
money)
:age(age), sex(sex), money(money)
{
std::cout <<
"call contrust"
<< std::endl;
}
CMan(CMan && other)
:age(other.age), sex(other.sex), money(other.money)
{
std::cout <<
"call move contrust"
<< std::endl;
}
};
int
main()
{
std::deque<CMan> dq;
dq.emplace_front(30,
"man"
, 12.3);
return
0;
}
|
可以看到,它就是利用了变参数模板的特性,传入了多个不同的构造入参,那么这些构造入参是怎么传入到类CMan本身的呢,我们看看函数emplace_front的源码实现,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#if __cplusplus >= 201103L
template
<
typename
_Tp,
typename
_Alloc>
template
<
typename
... _Args>
#if __cplusplus > 201402L
typename
deque<_Tp, _Alloc>::reference
#else
void
#endif
deque<_Tp, _Alloc>::
emplace_front(_Args&&... __args)
{
if
(
this
->_M_impl._M_start._M_cur !=
this
->_M_impl._M_start._M_first)
{
_Alloc_traits::construct(
this
->_M_impl,
this
->_M_impl._M_start._M_cur - 1,
std::forward<_Args>(__args)...);
--
this
->_M_impl._M_start._M_cur;
}
else
_M_push_front_aux(std::forward<_Args>(__args)...);
#if __cplusplus > 201402L
return
front();
#endif
}
|
可以看到,实际上是使用了std::forward来把形参包整个传递到内存分配器里面去,然后在内存分配器里面又通过调用operator new和std::forward把形参包传递给了容器的元素类型的构造函数.
std::forward意思是完美转发,可以把参数原封不动的传递下去.
这么一看,这不就是我们第4节里面说的形参包展开的第二种方法的一种实际使用案例吗,只是这里使用了std::forward实现了完美转发而已.
到此这篇关于c++11变参数模板的文章就介绍到这了,更多相关c++11变参数模板内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://mp.weixin.qq.com/s/0s9AGulcP1EjCz6iDHVeeQ 。
最后此篇关于一篇文章让你彻底明白c++11增加的变参数模板的文章就讲到这里了,如果你想了解更多关于一篇文章让你彻底明白c++11增加的变参数模板的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!