此模板代码如何获得数组的大小?


61

我想知道为什么这种代码可以得到测试数组的大小?我对模板中的语法不熟悉。也许有人可以解释下代码的含义template<typename,size_t>。此外,参考链接也是首选。

#define dimof(array) (sizeof(DimofSizeHelper(array)))
template <typename T, size_t N>
char(&DimofSizeHelper(T(&array)[N]))[N];

void InitDynCalls()
{
    char test[20];
    size_t n = dimof(test);
    printf("%d", n);
}

您是否阅读了有关C ++ 11的类似n3337的内容?它应该与您的问题有关!您是否考虑使用或....std::arraystd::vector
Basile Starynkevitch

@BasileStarynkevitch我还没看过。该代码显示在第三方库中。我只想弄清楚含义。
暗影魔王

另请参见norvig.com/21-days.html,以获取有用的见解(并查看谁是该页面的作者)。
巴士底·斯塔林凯维奇


@BasileStarynkevitch我不了解该链接的相关性。
Lightness Races in Orbit,

Answers:


86

这实际上是一个很难解释的问题,但我会尝试一下...

首先,dimof告诉您数组的维数或元素数。(我相信“维”是Windows编程环境中的首选术语)。

这是必要的,因为C++并且C没有给您提供确定数组大小的本地方法。


人们通常认为sizeof(myArray)会起作用,但这实际上会给您增加内存的大小,而不是元素的数量。每个元素可能占用超过1个字节的内存!

接下来,他们可以尝试sizeof(myArray) / sizeof(myArray[0])。这将给出数组内存的大小除以第一个元素的大小。没关系,并广泛用于C代码中。这样做的主要问题是,如果您传递指针而不是数组,它将看起来有效。指针在内存中的大小通常为4或8个字节,即使它指向的对象可能是1000个元素的数组。


因此,接下来要尝试的C++是使用模板来强制某些仅适用于数组的操作,并且会在指针上产生编译器错误。看起来像这样:

template <typename T, std::size_t N>
std::size_t ArraySize(T (&inputArray)[N])
{
    return N;
}
//...
float x[7];
cout << ArraySize(x); // prints "7"

该模板仅适用于数组。它将推断出类型(并非真正需要,但必须在那里才能使模板正常工作)和数组的大小,然后返回大小。模板的编写方式可能无法与指针一起使用。

通常您可以在这里停止,这在C ++标准库中为std::size


警告:在此处进入毛茸茸的语言律师领域。


这很酷,但是在晦涩的边缘情况下仍然失败:

struct Placeholder {
    static float x[8];
};

template <typename T, int N>
int ArraySize (T (&)[N])
{
    return N;
}

int main()
{
    return ArraySize(Placeholder::x);
}

注意,数组x声明的,但未定义。要定义一个函数(即ArraySize),x必须先定义

In function `main':
SO.cpp:(.text+0x5): undefined reference to `Placeholder::x'
collect2: error: ld returned 1 exit status

您无法链接。


您在问题中使用的代码可以解决该问题。我们声明了一个返回大小恰好正确的对象的函数,而不是实际调用函数。然后,我们sizeof在此使用技巧。

看起来像我们所说的功能,但sizeof纯粹是一个编译时间结构,因此函数实际上从未被调用。

template <typename T, size_t N>
char(&DimofSizeHelper(T(&array)[N]))[N];
^^^^ ^                               ^^^
// a function that returns a reference to array of N chars - the size of this array in memory will be exactly N bytes

注意,您实际上不能从函数返回数组,但是可以返回对数组的引用。

然后DimofSizeHelper(myArray)是一个表达式,类型N chars 上的数组。该表达式实际上不一定是可运行的,但在编译时是有意义的。

因此,sizeof(DimofSizeHelper(myArray))它将告诉您在编译时实际调用该函数所得到的大小。即使我们实际上不称呼它。

奥斯丁Powers斗眼


不要担心最后一块没有任何意义。解决一个奇怪的边缘情况是一个奇怪的把戏。这就是为什么您不自己编写此类代码,而让库实现者担心此类废话的原因。


3
@Shadowfiend这也是错误的。事情甚至比这更丑陋,因为它实际上不是函数的声明,而是函数引用的声明...我仍在弄清楚如何解释这一点。
BoBTFish

5
为什么它是函数引用的声明?根据Bolov的回答,“ DimofSizeHelper”之前的“&”表示返回类型为char(&)[N]。
暗影魔王

3
@Shadowfiend绝对正确。我只是在说垃圾,因为我的大脑被打结了。
BoBTFish

维度不是数组中元素的数量。也就是说,您可能具有1、2、3或更大尺寸的数组,每个数组可以具有相同数量的元素。例如,array1D [1000],array 2D [10] [100],array3D [10] [10] [10]。每个都有1000个元素。
jamesqf

1
@jamesqf在C ++之类的语言中,多维数组只是包含其他数组的数组。从编译器的角度来看,主数组中的元素数量通常与它的内容完全不相关-可能是辅助数组或三级数组。
Phlarx

27
template <typename T, size_t N>
char(&DimofSizeHelper(T(&array)[N]))[N];

// see it like this:
//                char(&DimofSizeHelper(T(&array)[N]))[N];
// template name:       DimofSizeHelper
// param name:                             array
// param type:                          T(&     )[N])
// return type:   char(&                             )[N];

DimofSizeHelper是一个模板函数,它接受一个T(&)[N]参数-也就是对N个类型为C的数组的引用,又T返回char (&)[N]对N个字符为数组的引用。在C ++中,char是伪装的字节,并由标准sizeof(char)保证1

size_t n = dimof(test);
// macro expansion:
size_t n = sizeof(DimofSizeHelper(array));

n被分配的返回类型的尺寸DimofSizeHelper,这是sizeof(char[N])这是N


这有点令人费解 和不必要的。通常的方法是:

template <class T, size_t N>
/*constexpr*/ size_t sizeof_array(T (&)[N]) { return N; }

由于C ++ 17,这也是不必要的,就像我们std::size这样做的那样,但是以更通用的方式,它能够获取任何stl样式的容器的大小。


正如BoBTFish指出的那样,对于边缘情况很有必要。


2
如果不能ODR-使用要取其大小的数组(则已声明但未定义),则很有必要。诚然,相当晦涩。
BoBTFish

感谢您解释模板功能中的类型。那真的有帮助。
暗影魔王

3
我们std::extent从C ++ 11开始就是编译时间。
LF
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.