请考虑以下代码:

template<typename T>
char (&f(T[1]))[1];

template<typename T>
char (&f(...))[2];

int main() { char c[sizeof(f<void()>(0)) == 2]; }

我期望它做SFINAE并选择第二次重载,因为将 T 替换为 T [1] 会产生

 void [1]()

当然,这是一种无效的类型。在将模板参数替换为函数参数并检查有效的结果类型(如14.8.2 [temp.deduct]描述)之后,完成参数类型(array-&gt;指针)的调整。

但是comeau和GCC都无法编译上述内容。两者都有不同的诊断。

Comeau说:

  

&quot; ComeauTest.c&quot;,第2行:错误:不允许函数数组 char(&amp; f(T [1]))[1];

GCC说(版本 4.3.3 ):

  

错误:ISO C ++禁止零大小数组 c

意思是,GCC不会替代,但是它会选择 f 的第一个重载,返回 sizeof 为1,而不是像以前那样替换它科莫。

什么编译器是正确的,我的代码是否有效?请参阅或引用答案中适当的标准部分。谢谢!


更新:标准本身在 14.8.2 / 2 的列表中包含这样的示例。我不知道为什么我先忽略它:

template <class T> int f(T[5]);
int I = f<int>(0);
int j = f<void>(0); // invalid array

虽然该示例仅提供信息,但它显示了所有这些神秘段落的意图,并且似乎表明上面的代码应该起作用并拒绝第一次重载。

有帮助吗?

解决方案

一个小小的音符,虽然非常罕见,但我发现了一些情况 相信Comeau编译器错了 - 尽管如此 场合是如此罕见,它总是值得加倍和三倍 检查你的假设!

我可能有一个g ++行为的原因。我不确定 在调整参数类型时确切指定:

请考虑以下事项:

template<typename T>
struct A
{
  void bar (T[10]);
};

template<typename T>
void A<T>::bar (T*)
{
}

'bar'的定义是合法的,因为“T [10]”衰变为“T *”。我做 没有看到禁止编译器的标准中的任何内容 根据模板声明执行8.3.5的调整, 它还可以提高过载匹配的性能。

将此应用于您的示例,g ++可能会将其视为:

template<typename T>
char (&f( T* ))[1];

template<typename T>
char (&f(...))[2];

int main() { char c[sizeof(f<void()>(0)) == 2]; }

在上面,替换参数是一个合法的指针 函数,而不是函数数组。

所以,对我来说问题是 - 是否有某些东西被禁止 功能参数(8.3.5)的调整两次?

就个人而言,我认为允许进行调整是有意义的 两次,否则它使功能模板的匹配变得复杂 重载

总之,我认为它对g ++有效,可以选择第一个重载 基于它如何处理衰减的数组参数,而Comeau是错误的 不要对函数数组进行演绎失败。

当然这现在意味着(如果Comeau已经修复)那么每个编译器 会选择不同的超载,仍然是标准 兼容! :(

修改

为了说明我的观点,请考虑以下代码:

template <typename T> void foo ( T * );
template <typename T> void foo ( T * const );
template <typename T> void foo ( T [] );
template <typename T> void foo ( T [10] );
template <typename T> void foo ( T [100] );

void bar () 
{
  foo < void() > ( 0 );
}

在这里,foo已经多次声明并重新声明。编译器应该使用哪种声明以及哪种参数类型应用14.8.2中列出的规则?

我的观点是该标准没有说明上述内容。我还要说,对此的任何措辞都必须将其保留为“未定义”。或“实施定义的”或行为。

其他提示

足够令人惊讶 - 这在VS2008中确实有效。我认为这不一定是正确行为的证据,但不是......

Visual Studio正在解释

char (&f(T[1]))[1];

作为一个函数,它接受一个T大小为1的数组,并返回一个大小为1的字符数组的引用。

我将尝试描述模板参数推导的过程,正如我从阅读标准中所理解的那样。

  1. 按照14.8.2 / 2中所述检查显式模板参数。
  2. 根据8.3.5调整得到的函数签名(即执行数组到指针衰减)。
  3. 根据14.8.2.1推导出隐式模板参数(这是对步骤2中部分替换的签名执行的。)
  4. 第一次重载的推断在步骤1中失败,因此重载决策返回第二次重载。我不相信这个节目是不正确的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top