【问题标题】:Is a function reference sizeof portable?函数参考 sizeof 是否可移植?
【发布时间】:2010-05-31 16:32:45
【问题描述】:

寻找一种方法来为 c 样式数组进行可移植、安全的元素计数,我找到了这个解决方案:

template <typename T, unsigned N>  char (&arrayCountofHelper(T(&)[N]))[N];
#define ARRAY_COUNTOF(arr) (sizeof(arrayCountofHelper(arr)))

看起来 arrayCountofHelper 实际上是对函数的引用,宏 ARRAY_COUNTOF 使用了函数的大小是它返回类型的大小这一事实。
它工作得很好。
但是,当我尝试检查它是否可移植时,我没有找到任何证据。我在标准 (14882/1998) 中找不到对 sizeof(function reference) 的任何引用,事实上,我不完全确定我什至可以再创建一个函数引用(尽管标准确实提到了几个次)。
那么,有人知道我应该在标准中看哪里吗? (或者,如果我以某种方式误解了声明,那么正确的解释是什么?)

谢谢
奥伦

附: (对于那些认为我没有找到适合我的问题的解决方案的人)
我知道我总是可以使用

#define ARRAY_COUNTOF sizeof(arr)/sizeof(arr[0])

甚至

template <typename T, unsigned N> size_t arrayCountof(T(&)[N]) {return N;}

但第一个不会检查 arr 是否为数组(或指针),第二个在 static_assert 中不可用。
(我会使用 std::tr1::array 或 std::vector,但这是我正在维护的遗留代码)

【问题讨论】:

标签: c++ templates function


【解决方案1】:

我们将松散地分开这个,我使用的是 INCITS+ISO+IEC+14882-2003。我会引用小的东西,但一些更复杂的东西太大而无法引用。

sizeof 在 §5.3.3 中定义,它说(删节):

sizeof 运算符产生其操作数的对象表示中的字节数。操作数要么是一个未计算的表达式,要么是一个带括号的类型 ID。

换句话说,它产生以字节为单位的类型的大小,或者找到表达式的类型并产生它的大小。我们没有类型,我们有表达式arrayCountofHelper(arr)

您可以通过查看分别在 §5.1 和 §5.2 中定义的 primary-expressionpostfix-expression 的定义来剖析这个表达式。您会发现它是 postfix-expression,并且符合函数调用的要求(第 5.2.2 节)。

现在回到sizeof。我们只关心这个函数调用表达式的类型(所以我们可以得出它的大小),§5.2.2/3 说:

函数调用表达式的类型是静态选择函数的返回类型 [...]。该类型应为完整的对象类型、引用类型或void类型。

所以我们需要找到将使用arrayCountofHelper(arr) 调用的函数的返回类型(记住,这都是未计算的)。 arrayCountofHelper 是我们将要实例化的函数模板(第 14.7 节),因此我们需要在实例化函数之前执行此操作以获取其返回类型。

所有模板参数都需要有值(第 14.8.2 节),通过使用第 14.8.2.1 节中定义的规则,我们将通过将传递给函数的数组与函数匹配来找到 TN参数(这是对数组的引用)。 (例如,如果 arrint[10]T 将是 intN 将是 10。)一旦我们有了这些,函数就可以被实例化。

一旦实例化,函数的返回类型将是char(&amp;)[N]*,这是对Nchar的数组的引用。 (如果您需要帮助解析,请参阅§8.3.5。还有关于如何解析“复杂”类型的问题。)所以现在我们已经找到了表达式的类型,我们必须取它的大小。

§5.3.3/2 定义了 sizeof 如何与引用和数组一起使用(强调我的):

当应用于引用或引用类型时,结果是被引用类型的大小。 当应用于类时,结果是该类的对象中的字节数,包括将该类型的对象放入数组中所需的任何填充。最派生类的大小应大于零 (1.8)。将 sizeof 应用于基类子对象的结果是基类类型的大小。70) 应用于数组时,结果是数组中的总字节数。这意味着包含 n 个元素的数组的大小是元素大小的 n 倍。

引用类型的大小就是它被引用类型的大小,所以我们需要大小为char[N]。这个大小是N * sizeof(char)char 是基本的,因为它是最小的类型;也就是说,sizeof(char)总是之一。 (§5.3.3/1) 所以这个表达式产生的大小是1 * N,或者我们一直想要的:N

这就是它的工作原理。


这个比arrayCountof 的最后一个例子更受欢迎的原因是sizeof 的结果是一个常量表达式,所以可以在需要常量表达式的地方使用。

需要注意的是,在 C++0x 中,我们可以通过以下方式获得干净的非宏语法:

template <typename T, unsigned N>
constexpr size_t arrayCountof(T(&)[N]) {return N;}

* 函数返回类型是对数组而不是数组的引用的原因仅仅是因为你不能返回数组。如果可以的话,任何一种选择都足够了。

【讨论】:

  • +1 :) 说template &lt;typename T, unsigned N&gt; typename identity&lt;char[N]&gt;::type &amp;arrayCountofHelper(T(&amp;)[N]); 可以使整个事情看起来更好看 :) 不过我认为 sizeof 运算符的描述很奇怪,因为表达式从不具有引用类型,因为 @ 987654351@(所以sizeof操作符得到的表达式的类型是char[N]),所以它应该指定“当应用于引用类型时,结果是被引用类型的大小”。 - 没有提及参考。最后,由于引用通过,无论如何都没关系:)
  • 哇,真的应该添加这个 +100 按钮 :) 谢谢!
【解决方案2】:

欧伦,

这不是函数引用。这是一个函数调用表达式,它有一个类型——结果的类型。 Sizeof 可以应用于任何具有类型的表达式。

【讨论】:

    猜你喜欢
    • 2021-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多