How do I use arrays in C++?
C++从C继承数组,在那里几乎到处使用。C++提供了更易于使用和更容易出错的抽象(自从C++ 98和EDCOX1 1以来,EDCOX1是0),所以对数组的需求并不像C中那样频繁出现。然而,当你读取遗留代码或与C语言编写的库交互时,你应该牢牢掌握数组是如何工作的。
本常见问题分为五部分:
如果您觉得本常见问题解答中缺少一些重要内容,请写一个答案并将其作为附加部分链接到此处。
在下面的文本中,"array"表示"c array",而不是类模板
类型级别上的数组
数组类型表示为
1 2 3 4 | #include <type_traits> static_assert(!std::is_same<int[8], float[8]>::value,"distinct element type"); static_assert(!std::is_same<int[8], int[9]>::value,"distinct size"); |
请注意,大小是类型的一部分,也就是说,不同大小的数组类型是不兼容的类型,它们彼此之间完全没有任何关系。
1 2 3 4 5 6 7 8 | +---+---+---+---+---+---+---+---+ the_actual_array: | | | | | | | | | int[8] +---+---+---+---+---+---+---+---+ ^ | | | | pointer_to_the_first_element int* |
这种转换被称为"数组到指针的衰减",它是造成混淆的主要原因。在此过程中,数组的大小将丢失,因为它不再是类型的一部分(
每当认为数组的第一个元素有用时,即当操作在数组上失败但在指针上成功时,编译器将自动生成指向该元素的指针。从数组到指针的转换很简单,因为得到的指针值只是数组的地址。请注意,指针不是作为数组本身(或内存中的任何其他地方)的一部分存储的。数组不是指针。好的。
1 | static_assert(!std::is_same<int[8], int*>::value,"an array is not a pointer"); |
数组不衰减为指向其第一个元素的指针的一个重要上下文是当
1 | static_assert(!std::is_same<int*, int(*)[8]>::value,"distinct element type"); |
以下ASCII艺术解释了这一区别:好的。
1 2 3 4 5 6 7 8 9 10 11 | +-----------------------------------+ | +---+---+---+---+---+---+---+---+ | +---> | | | | | | | | | | | int[8] | | +---+---+---+---+---+---+---+---+ | | +---^-------------------------------+ | | | | | | | | pointer_to_the_first_element int* | | pointer_to_the_entire_array int(*)[8] |
请注意,指向第一个元素的指针只指向一个整数(表示为一个小方框),而指向整个数组的指针指向一个8个整数的数组(表示为一个大方框)。好的。
同样的情况也出现在课堂上,而且可能更明显。指向对象的指针及其第一个数据成员的指针具有相同的值(相同的地址),但它们是完全不同的类型。好的。
如果您不熟悉C声明符语法,那么类型
int(*)[8] 是指向8个整数数组的指针。int*[8] 是一个由8个指针组成的数组,每个指针都是int* 类型的元素。
访问元素
C++提供了两个语法变量来访问数组的各个元素。两者都不优于对方,你应该熟悉两者。好的。指针算术
给定指向数组第一个元素的指针
1 | std::cout << *(x+3) <<"," << *(x+7) << std::endl; |
如果
1 2 3 4 5 6 7 8 | +---+---+---+---+---+---+---+---+ x: | | | | | | | | | int[8] +---+---+---+---+---+---+---+---+ ^ ^ ^ | | | | | | | | | x+0 | x+3 | x+7 | int* |
(注意,隐式生成的指针没有名称,所以我编写
另一方面,如果
1 2 3 4 5 6 7 8 9 | +---+---+---+---+---+---+---+---+ | | | | | | | | | int[8] +---+---+---+---+---+---+---+---+ ^ ^ ^ | | | | | | +-|-+ | | x: | | | x+3 | x+7 | int* +---+ |
注意,在所描述的情况下,
由于语法EDOCX1 OR 13是有点笨拙的,C++提供了另一种语法
1 | std::cout << x[3] <<"," << x[7] << std::endl; |
由于加法是可交换的,因此以下代码的作用完全相同:好的。
1 | std::cout << 3[x] <<"," << 7[x] << std::endl; |
索引运算符的定义导致以下有趣的等价性:好的。
1 | &x[i] == &*(x+i) == x+i |
但是,
1 2 | T* p = &array[0]; // rewritten as &*(array+0), decay happens due to the addition T* q = array; // decay happens due to the assignment |
在第一行,编译器检测到从指针到指针的赋值,这是非常简单的成功。在第二行,它检测到从数组到指针的赋值。由于这是无意义的(但指针到指针的分配是有意义的),数组到指针的衰减像往常一样开始。好的。范围
1 2 3 4 5 6 7 8 | +---+---+---+---+---+---+---+---+.... x: | | | | | | | | | . int[8] +---+---+---+---+---+---+---+---+.... ^ ^ | | | | | | x+0 | x+8 | int* |
例如,如果要对数组进行排序,以下两种方法都同样有效:好的。
1 2 | std::sort(x + 0, x + n); std::sort(&x[0], &x[0] + n); |
请注意,将EDCOX1作为0个参数作为第二个参数是非法的,因为这相当于EDCOX1 OR 1,并且子表达式EDCOX1 OR 2在技术上调用C++中的未定义行为(但不在C99中调用)。好的。
还要注意,您可以简单地提供
程序员经常混淆多维数组和指针数组。
多维数组大多数程序员都熟悉命名的多维数组,但许多人不知道多维数组也可以匿名创建。多维数组通常被称为"数组"或"真正的多维数组"。
命名多维数组使用命名多维数组时,所有维度都必须在编译时已知:
1 2 3 4 5 6 7 8 | int H = read_int(); int W = read_int(); int connect_four[6][7]; // okay int connect_four[H][7]; // ISO C++ forbids variable length array int connect_four[6][W]; // ISO C++ forbids variable length array int connect_four[H][W]; // ISO C++ forbids variable length array |
这是命名多维数组在内存中的外观:
1 2 3 4 5 6 7 8 9 10 11 12 13 | +---+---+---+---+---+---+---+ connect_four: | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | +---+---+---+---+---+---+---+ |
请注意,上面这样的二维网格仅仅是有帮助的可视化。从C++的观点来看,内存是字节的"扁平"序列。多维数组的元素按行主顺序存储。也就是说,
1 2 3 | int* p = &connect_four[0][0]; int* q = p + 42; some_int_sequence_algorithm(p, q); |
匿名多维数组
对于匿名多维数组,除第一个之外的所有维度都必须在编译时已知:
1 2 3 4 5 | int (*p)[7] = new int[6][7]; // okay int (*p)[7] = new int[H][7]; // okay int (*p)[W] = new int[6][W]; // ISO C++ forbids variable length array int (*p)[W] = new int[H][W]; // ISO C++ forbids variable length array |
这就是在内存中匿名多维数组的样子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | +---+---+---+---+---+---+---+ +---> | | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | | | +---+---+---+---+---+---+---+ | | | | | | | | | | +---+---+---+---+---+---+---+ | +-|-+ p: | | | +---+ |
请注意,数组本身仍然作为内存中的单个块分配。
指针数组您可以通过引入另一个间接级别来克服固定宽度的限制。
指针的命名数组下面是一个由五个指针组成的命名数组,这些指针由不同长度的匿名数组初始化:
1 2 3 4 5 6 7 8 9 10 11 12 | int* triangle[5]; for (int i = 0; i < 5; ++i) { triangle[i] = new int[5 - i]; } // ... for (int i = 0; i < 5; ++i) { delete[] triangle[i]; } |
这就是它在记忆中的样子:
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 | +---+---+---+---+---+ | | | | | | +---+---+---+---+---+ ^ | +---+---+---+---+ | | | | | | | +---+---+---+---+ | ^ | | +---+---+---+ | | | | | | | | +---+---+---+ | | ^ | | | +---+---+ | | | | | | | | | +---+---+ | | | ^ | | | | +---+ | | | | | | | | | | +---+ | | | | ^ | | | | | | | | | | +-|-+-|-+-|-+-|-+-|-+ triangle: | | | | | | | | | | | +---+---+---+---+---+ |
由于现在每一行都是单独分配的,因此将二维数组视为一维数组不再有效。
指针的匿名数组这里是一个由5个(或任何其他数量)指针组成的匿名数组,这些指针由不同长度的匿名数组初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | int n = calculate_five(); // or any other number int** p = new int*[n]; for (int i = 0; i < n; ++i) { p[i] = new int[n - i]; } // ... for (int i = 0; i < n; ++i) { delete[] p[i]; } delete[] p; // note the extra delete[] ! |
这就是它在记忆中的样子:
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 | +---+---+---+---+---+ | | | | | | +---+---+---+---+---+ ^ | +---+---+---+---+ | | | | | | | +---+---+---+---+ | ^ | | +---+---+---+ | | | | | | | | +---+---+---+ | | ^ | | | +---+---+ | | | | | | | | | +---+---+ | | | ^ | | | | +---+ | | | | | | | | | | +---+ | | | | ^ | | | | | | | | | | +-|-+-|-+-|-+-|-+-|-+ | | | | | | | | | | | +---+---+---+---+---+ ^ | | +-|-+ p: | | | +---+ |
转换
数组到指针的衰减自然扩展到数组和指针数组:
1 2 3 4 5 | int array_of_arrays[6][7]; int (*pointer_to_array)[7] = array_of_arrays; int* array_of_pointers[6]; int** pointer_to_pointer = array_of_pointers; |
但是,没有从
1 2 3 4 5 6 7 8 9 10 11 | int connect_four[6][7]; int** p = new int*[6]; for (int i = 0; i < 6; ++i) { p[i] = connect_four[i]; } // ... delete[] p; |
请注意,这将生成原始多维数组的视图。如果您需要一个副本,您必须创建额外的数组并自己复制数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | int connect_four[6][7]; int** p = new int*[6]; for (int i = 0; i < 6; ++i) { p[i] = new int[7]; std::copy(connect_four[i], connect_four[i + 1], p[i]); } // ... for (int i = 0; i < 6; ++i) { delete[] p[i]; } delete[] p; |
转让
由于没有特殊原因,数组不能彼此分配。用
1 2 3 4 5 6 7 | #include // ... int a[8] = {2, 3, 5, 7, 11, 13, 17, 19}; int b[8]; std::copy(a + 0, a + 8, b); |
这比真正的数组分配提供的更灵活,因为可以将较大数组的切片复制到较小的数组中。
虽然不能直接分配数组,但可以分配包含数组成员的结构和类。这是因为数组成员是由编译器默认提供的赋值运算符以成员方式复制的。如果为自己的结构或类类型手动定义赋值运算符,则必须返回到数组成员的手动复制。
参数传递数组不能按值传递。您可以通过指针或引用传递它们。
传递指针因为数组本身不能按值传递,所以通常指向第一个元素的指针是按值传递的。这通常被称为"传递指针"。由于数组的大小不能通过该指针检索,所以必须传递指示数组大小的第二个参数(经典C解决方案)或指向数组最后元素(C++迭代器解决方案)之后的第二指针:
1 2 3 4 5 6 7 8 9 10 11 12 | #include <numeric> #include <cstddef> int sum(const int* p, std::size_t n) { return std::accumulate(p, p + n, 0); } int sum(const int* p, const int* q) { return std::accumulate(p, q, 0); } |
作为一种语法选择,您还可以将参数声明为
1 2 3 4 | int sum(const int p[], std::size_t n) { return std::accumulate(p, p + n, 0); } |
您可以认为编译器只是在参数列表的上下文中将
不幸的是,您还可以在数组参数中提供一个大小,编译器会自动忽略该大小。也就是说,以下三个签名完全等效,如编译器错误所示:
1 2 3 4 5 6 7 | int sum(const int* p, std::size_t n) // error: redefinition of 'int sum(const int*, size_t)' int sum(const int p[], std::size_t n) // error: redefinition of 'int sum(const int*, size_t)' int sum(const int p[8], std::size_t n) // the 8 has no meaning here |
通过引用
数组也可以通过引用传递:
1 2 3 4 | int sum(const int (&a)[8]) { return std::accumulate(a + 0, a + 8, 0); } |
在这种情况下,数组大小很重要。由于编写一个只接受8个元素数组的函数几乎没有什么用处,程序员通常编写模板这样的函数:
1 2 3 4 5 | template <std::size_t n> int sum(const int (&a)[n]) { return std::accumulate(a + 0, a + n, 0); } |
请注意,只能使用实际的整数数组调用这样的函数模板,而不能使用指向整数的指针。数组的大小是自动推断的,对于每个大小的
数组创建和初始化
与任何其他类型的C++对象一样,数组可以直接存储在命名变量中(然后,大小必须是编译时常数;C++不支持VLAs),或者它们可以匿名地存储在堆上,并通过指针间接访问(只有在运行时才能计算大小)。好的。自动阵列
每次控制流通过非静态局部数组变量定义时,都会创建自动数组(数组位于堆栈上):好的。
1 2 3 4 | void foo() { int automatic_array[8]; } |
初始化按升序执行。注意,初始值取决于元素类型
- 如果
T 是一个pod(如上述示例中的int ),则不会进行初始化。 - 否则,EDOCX1的默认构造函数将初始化所有元素。
- 如果
T 没有提供可访问的默认构造函数,则程序不编译。
或者,可以在数组初始值设定项中显式指定初始值,数组初始值设定项是一个逗号分隔的列表,由大括号包围:好的。
1 | int primes[8] = {2, 3, 5, 7, 11, 13, 17, 19}; |
由于在这种情况下,数组初始值设定项中的元素数等于数组的大小,因此手动指定大小是多余的。它可以由编译器自动推断:好的。
1 | int primes[] = {2, 3, 5, 7, 11, 13, 17, 19}; // size 8 is deduced |
也可以指定大小并提供较短的数组初始值设定项:好的。
1 | int fibonacci[50] = {0, 1, 1}; // 47 trailing zeros are deduced |
在这种情况下,其余的元素被初始化为零。请注意,C++允许空数组初始化器(所有元素都为零初始化),而C89不(至少需要一个值)。还要注意,数组初始值设定项只能用于初始化数组;以后不能在赋值中使用它们。好的。静态数组
静态数组(位于"数据段"中的数组)是使用
1 2 3 4 5 6 | int global_static_array[8]; void foo() { static int local_static_array[8]; } |
(请注意,命名空间范围内的变量是隐式静态的。将
下面是静态数组与自动数组的行为不同之处:好的。
- 没有数组初始值设定项的静态数组在任何进一步的潜在初始化之前都是零初始化的。
- 静态pod数组初始化一次,初始值通常烘焙到可执行文件中,在这种情况下,在运行时不需要初始化成本。然而,这并不总是最节省空间的解决方案,而且标准并不要求这样做。
- 静态非pod数组在控制流第一次通过其定义时初始化。对于局部静态数组,如果从未调用函数,则可能永远不会发生这种情况。
(以上都不是特定于数组的。这些规则同样适用于其他类型的静态对象。)好的。阵列数据成员
are created when their members数组数据对象owning is created。不幸的是他/她,C++ 03提供不到固体聚合物arrays均值初始化初始化列表,我initialization must be with assignments:faked>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Foo { int primes[8]; public: Foo() { primes[0] = 2; primes[1] = 3; primes[2] = 5; // ... } }; |
你可以定义alternatively,安自动拷贝构造函数体阵列在茶叶和茶元过:>
1 2 3 4 5 6 7 8 9 10 11 12 | class Foo { int primes[8]; public: Foo() { int local_array[] = {2, 3, 5, 7, 11, 13, 17, 19}; std::copy(local_array + 0, local_array + 8, primes + 0); } }; |
在C++0x中,arrays can be the成员初始化列表initialized thanks to initialization一致:>
1 2 3 4 5 6 7 8 9 10 | class Foo { int primes[8]; public: Foo() : primes { 2, 3, 5, 7, 11, 13, 17, 19 } { } }; |
this is the only solution types that have with元,工厂没有默认构造函数。>
动态arrays
因此,有没有动态arrays names of the only均值,accessing them is分离开。因为他们都没有名字,他们会推荐为"匿名arrays"从现在。>
在C,arrays are created
1 2 | std::size_t size = compute_size_at_runtime(); int* p = new int[size]; |
ASCII艺术depicts the following the memory if the size is as布局8在计算机运行时:>
1 2 3 4 5 6 7 8 9 | +---+---+---+---+---+---+---+---+ (anonymous) | | | | | | | | | +---+---+---+---+---+---+---+---+ ^ | | +-|-+ p: | | | int* +---+ |
明显的,匿名的要求更多的内存比arrays arrays命名两个that must be to the separately存储额外的指针。(there is also some on the additional量免税商店)。>
注that there is不衰减了指针阵列-在线这里。尽管事实上建立安评教
关于元素的默认值为,arrays similar to自动arrays匿名的举止。normally are not initialized匿名arrays,POD,but that there is a special initialization触发器表值:>
1 | int* p = new int[some_computed_size](); |
(注trailing pair of the before the right semicolon性括号。)再次,C++1x simplifies the rules and initial values for allows specifying arrays thanks to一致initialization:匿名>
1 | int* p = new int[8] { 2, 3, 5, 7, 11, 13, 17, 19 }; |
如果你是做安阵列使用匿名,You have to back to the en:缓释系统>
1 | delete[] p; |
你必须释放每盎司不确切的说因为是匿名的阵列,然后又afterwards触摸。结果:在所有不释放在存储漏(或更多generally depending on the元,资源型,漏),和试图释放在恩多时报Undefined行为的结果。using the形态(或非阵列
好吧。
5。使用数组时常见的陷阱。5.1陷阱:信任型不安全链接。
好吧,您已经被告知或发现了全局(命名空间可在翻译单元之外访问的范围变量邪恶与贸易;但你知道他们是多么邪恶吗?考虑一下下面的程序,由两个文件[main.cpp]和[numbers.cpp]组成:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // [main.cpp] #include <iostream> extern int* numbers; int main() { using namespace std; for( int i = 0; i < 42; ++i ) { cout << (i > 0?"," :"") << numbers[i]; } cout << endl; } |
好的。
1 2 | // [numbers.cpp] int numbers[42] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; |
在Windows7中,这与mingw g++4.4.1和Visual C++ 10。好的。
因为类型不匹配,所以运行程序时程序会崩溃。好的。
好的。
在正式解释中:程序有未定义的行为(ub),而不是所以它可以挂起来,或者什么也不做,或者它可以给美国、俄罗斯、印度总统发三封电子邮件,中国和瑞士,让鼻腔守护者飞出你的鼻子。好的。
在实践中说明:在
编译器完全有权不诊断此错误,因为C++ 11×3.5/10说明了兼容类型的要求。对于声明,好的。
[N3290 §3.5/10]
A violation of this rule on type identity does not require a diagnostic.Ok.
同一段详细说明了允许的变更:好的。
… declarations for an array object can specify array types that
differ by the presence or absence of a major array bound (8.3.4).Ok.
允许的变体不包括将名称声明为一个数组翻译单元,作为另一个翻译单元的指针。好的。5.2陷阱:过早优化(
还没有写完好的。5.3陷阱:使用C习语获取元素数量。
有了丰富的C经验,书写&hellip;是很自然的事。好的。
1 | #define N_ITEMS( array ) (sizeof( array )/sizeof( array[0] )) |
由于
主要缺陷:C习语不是类型安全的。例如,代码和Helip;好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h> #define N_ITEMS( array ) (sizeof( array )/sizeof( *array )) void display( int const a[7] ) { int const n = N_ITEMS( a ); // Oops. printf("%d elements. ", n ); } int main() { int const moohaha[] = {1, 2, 3, 4, 5, 6, 7}; printf("%d elements, calling display... ", N_ITEMS( moohaha ) ); display( moohaha ); } |
将指针传递给
7个元素,调用显示…1要素。好的。< /块引用>
为了在运行时检测此错误,您可以执行&hellip;好的。
1 2 3 4 5 6 7 8 9 10 | #include #include <typeinfo> #define N_ITEMS( array ) ( \ assert(( \ "N_ITEMS requires an actual array as argument", \ typeid( array ) != typeid( &*array ) \ )), \ sizeof( array )/sizeof( *array ) \ ) |
7 elements, calling display...
Assertion failed: ("N_ITEMS requires an actual array as argument", typeid( a ) != typeid( &*a ) ), file runtime_detect
ion.cpp, line 16Ok.
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.Ok.
运行时错误检测比不检测好,但有点浪费处理器时间,也许还有更多的程序员时间。更好的检测编译时间!如果您不支持用C++ 98支持本地类型的数组,然后你可以这样做:好的。
1 2 3 4 5 6 7 8 | #include <stddef.h> typedef ptrdiff_t Size; template< class Type, Size n > Size n_items( Type (&)[n] ) { return n; } #define N_ITEMS( array ) n_items( array ) |
用g++把这个定义编译成第一个完整的程序,我得到了&好的。
M:\count> g++ compile_time_detection.cpp
compile_time_detection.cpp: In function 'void display(const int*)':
compile_time_detection.cpp:14: error: no matching function for call to 'n_items(const int*&)'Ok.
M:\count> _
Ok.
工作原理:数组是通过引用
用C++ 11,您也可以使用本地类型的数组,它是类型安全的。用于查找数组元素数的C++习语。好的。5.4 C++ 11和C++ 14陷阱:使用EDCOX1×9数组大小函数。
用C++ 11,以后它是自然的,但是你会看到危险的!,为了替换C++ 03函数好的。
1 2 3 4 | typedef ptrdiff_t Size; template< class Type, Size n > Size n_items( Type (&)[n] ) { return n; } |
具有好的。
1 2 3 4 | using Size = ptrdiff_t; template< class Type, Size n > constexpr auto n_items( Type (&)[n] ) -> Size { return n; } |
其中重大变化是使用
例如,与C++ 03函数相反,这样的编译时间常数可用于声明与另一个数组大小相同的数组:好的。
1 2 3 4 5 6 7 8 | // Example 1 void foo() { int const x[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4}; constexpr Size n = n_items( x ); int y[n] = {}; // Using y here. } |
但是考虑使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Example 2 template< class Collection > void foo( Collection const& c ) { constexpr int n = n_items( c ); // Not in C++14! // Use c here } auto main() -> int { int x[42]; foo( x ); } |
陷阱:截至2015年7月,上述内容与mingw-64 5.1.0进行了比较。
A conditional-expression
e is a core constant expression unless the evaluation
ofe , following the rules of the abstract machine (1.9), would evaluate one of the
following expressions:
?Ok.
- an id-expression that refers to a variable or data member of reference type
unless the reference has a preceding initialization and either
- it is initialized with a constant expression or
- it is a non-static data member of an object whose lifetime began within
the evaluation of e;
一个人总是可以写得更详细好的。
1 2 3 4 5 6 7 8 9 10 | // Example 3 -- limited using Size = ptrdiff_t; template< class Collection > void foo( Collection const& c ) { constexpr Size n = std::extent< decltype( c ) >::value; // Use c here } |
&hellip;但当
要处理可以是非数组的集合,需要
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 | // Example 4 - OK (not ideal, but portable and safe) #include #include <stddef.h> using Size = ptrdiff_t; template< Size n > struct Size_carrier { char sizer[n]; }; template< class Type, Size n > auto static_n_items( Type (&)[n] ) -> Size_carrier<n>; // No implementation, is used only at compile time. template< class Type, size_t n > // size_t for g++ auto static_n_items( std::array<Type, n> const& ) -> Size_carrier<n>; // No implementation, is used only at compile time. #define STATIC_N_ITEMS( c ) \ static_cast<Size>( sizeof( static_n_items( c ).sizer ) ) template< class Collection > void foo( Collection const& c ) { constexpr Size n = STATIC_N_ITEMS( c ); // Use c here (void) c; } auto main() -> int { int x[42]; std::array<int, 43> y; foo( x ); foo( y ); } |
关于
关于命名:
希望你的问题能得到解决。C++ 17,但在此之前,像EDCOX1(2)以上的宏产生可移植性,例如,对于CLAN和Visual C++编译器,保留类型安全。好的。
相关:宏不考虑作用域,因此为了避免名称冲突,它可以是最好使用名称前缀,例如