SFINAE 具有无效的函数类型或数组类型参数?
请考虑这段代码:
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]()
Which 当然是无效类型。 在将模板参数替换为函数参数并检查有效的结果类型(如 14.8.2 [temp.deduct] 描述)之后,完成参数类型(数组 -> 指针)的调整。
但是comeau和GCC都无法编译上述内容。 两者都有不同的诊断。
科莫 说:
“ComeauTest.c”,第 2 行:错误:不允许使用函数数组
char (&f(T[1]))[1];
GCC 说(版本 4.3.3 ):
错误:ISO C++ 禁止零大小数组
c
意思是,GCC 不会替换失败,但它选择了 f
的第一个重载,返回一个 sizeof 为 1,而不是像 Comeau 一样预先替换它。
哪种编译器是正确的?我的代码是否有效? 请在您的答案中参考或引用适当的标准部分。 谢谢!
更新:标准本身在 14.8.2/2
的列表中包含这样的示例。 我不知道为什么我首先忽略了它:
template <class T> int f(T[5]);
int I = f<int>(0);
int j = f<void>(0); // invalid array
虽然这个示例只是提供信息,但它显示了所有这些神秘段落的意图,并且似乎表明上面的代码应该可以工作并拒绝第一个重载。
Please consider this code:
template<typename T>
char (&f(T[1]))[1];
template<typename T>
char (&f(...))[2];
int main() { char c[sizeof(f<void()>(0)) == 2]; }
I expected it doing SFINAE and chosing the second overload, since substitution of T
into T[1]
yields
void [1]()
Which is an invalid type, of course. Adjustment of parameter types (array->pointer) is done after substituting template parameters into function parameters and checking for valid resulting types like 14.8.2 [temp.deduct] describes.
But both comeau and GCC fail to compile the above. Both with different diagnostics.
Comeau says:
"ComeauTest.c", line 2: error: array of functions is not allowed
char (&f(T[1]))[1];
GCC says (version 4.3.3
):
error: ISO C++ forbids zero-size array
c
Meaning, GCC does not fail to substitute, but it chooses the first overload of f
, returning a sizeof
of 1, instead of failing to substitute it up front like Comeau.
What compiler is right and is my code valid at all? Please refer to or quote the proper Standard section in your answer. Thanks!
Update: The Standard itself contains such an example in the list at 14.8.2/2
. I don't know, why I overlooked it first:
template <class T> int f(T[5]);
int I = f<int>(0);
int j = f<void>(0); // invalid array
While the example is only informative, it shows the intention of all those mysterious paragraphs and seems to show the code above should work and reject the first overload.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一个小纸条,虽然很罕见,但我发现过一些场合
相信 Comeau 编译器有错误 - 尽管,这些
机会如此难得,它总是值得双倍和三倍
检查你的假设!
我可能对 g++ 的行为有一个原因。 我不确定它是
在调整参数类型时准确指定:
请考虑以下事项:
“bar”的定义是合法的,因为“T[10]”衰减为“T*”。 我愿意
在标准中没有看到任何禁止编译器的内容
针对模板声明执行 8.3.5 的调整,
而且它还提高了过载匹配时的性能。
将其应用到您的示例中,g++ 可能会将其视为:
在上面,替换的参数是一个合法的指针
函数,而不是函数数组。
所以,对我来说,问题是——是否有什么东西禁止
功能参数(8.3.5)调整两次?
就我个人而言,我认为允许进行调整是有意义的
两次,否则会使函数模板的匹配变得复杂
总之
,我认为 g++ 选择第一个重载是有效的
基于它如何处理衰减数组参数,Comeau 是错误的
函数数组不会出现演绎失败。
当然,这现在意味着(如果 Comeau 已修复)那么每个编译器
会选择不同的过载并且仍然是标准
符合! :(
编辑:
只是为了说明我的观点,请考虑以下代码:
这里, foo 已被声明并重新声明了几次。编译器应该应用哪个声明以及哪个参数类型,应用列出的规则14.8.2?
我的观点是,该标准没有对上述内容进行任何说明,我什至想说,任何有关此的措辞都必须将其保留为“未定义”或“实现定义”行为。
A small note, although very rare, I have found some occasions where I
believe that the Comeau compiler has it wrong - although, these
occasions are so rare that its always worth double and triple
checking your assumptions!
I may have a reason for the behaviour of g++. I'm not sure its
specified exactly when parameter types are adjusted:
Consider the following:
The definition of 'bar' is legal, as "T[10]" decays to "T*". I do
not see anything in the standard that prohibits the compiler from
performing the adjustments of 8.3.5 against the template declaration,
and it also improves performance when it comes to overload matching.
Applying this to your example, g++ might be treating it as:
In the above, the substituted parameter is a legal pointer to
function, rather than an array of functions.
So, the question for me is - is if there is something that prohibts
the adjustments for the function parameters (8.3.5) twice?
Personally, I think it makes sense to allow the adjustments to happen
twice since otherwise it complicates the matching of function template
overloads
In conclusion, I think its valid for g++ to select the first overload
based on how it treates decaying array parameters, and Comeau is wrong
not to have a deduction failure for the array of functions.
Of course this now means that (if Comeau was fixed) then each compiler
would choose a different overload and would still be standards
compliant! :(
EDIT:
Just to illustrate my point, consider the following code:
Here, foo has been declared and redeclared several times. Which declaration, and so which parameter type, should the compiler apply the rules listed in 14.8.2?
My point is that the standard doesn't say anything about the above. I would also go as far as to say that any wording on this would have to leave it as either "undefined" or "implementation defined" behaviour.
令人惊讶的是——这在 VS2008 中确实有效。 我认为这不一定是其行为正确与否的证据...
Visual Studio 将其解释
为一个函数,该函数接受大小为 1 的 T 数组,并返回对大小为 1 的字符数组的引用。
Suprisingly enough - this does work in VS2008. I don't think that's necessarily evidence for it being correct behaviour or not though...
Visual Studio is interpretting
as a function that takes an array of size 1 of T, and returns a reference to an array of chars of size 1.
我将尝试描述我通过阅读标准所理解的模板参数推导的过程。
第一个重载的推导在步骤 1 中失败,因此重载决策返回第二个重载。 我不认为该程序格式不正确。
I'll try to describe the process of template argument deduction as I understand it from reading the standard.
The deduction for the first overload fails in step 1, the overload resolution therefore returns the second overload. I don't believe the program is ill-formed.