我试图学习如何使用" new" C11通用表达式,但我碰到了一堵墙。
请考虑以下代码:
#include <stdlib.h>
#include <stdio.h>
#define test(X, Y, c) \
_Generic((X), \
double: _Generic((Y), \
double * : test_double, \
default: test_double \
), \
int: _Generic((Y), \
int * : test_int, \
default: test_int \
) \
) (X, Y, c)
int test_double(double a, double *b, int c);
int test_int(int a, int *b, int c);
int test_double(double a, double *b, int c) { return 1; }
int test_int(int a, int *b, int c) { return 2; }
int main()
{
double *t = malloc(sizeof(double));
int *s = malloc(sizeof(int));
int a1 = test(3.4, t, 1);
int i = 3;
int a2 = test(i, s, 1);
printf("%d\t", a1);
printf("%d\n", a2);
return 0;
}
这一切都完美无缺,但我还是不明白为什么这些默认情况在&#34; _Generic((Y),...&#34;是必要的,而我可以在结束时省略它&#34; _Generic((X),...&#34;没有后果。
事实上,如果我删除这两个默认值,我会收到错误(gcc 5.4.0),说&#34;类型'double *'的选择器与任何关联都不兼容&#34; 而宏观扩张&#34; int a1 = test(3.4,t,1);&#34;与&#34; int *&#34;相同的事情而宏扩展 test(i,s,1)
&#34;默认&#34;真的有必要还是我错过了什么? 在第一种情况下,为什么它应该是?如果我只有test_double和test_int可以被调用,为什么我应该为一些永远不应该编译的东西设置一个默认情况呢?
答案 0 :(得分:5)
_Generic
在标准中没有明确规定。通常的解释似乎是非选定案例中的表达式不得包含任何约束违规。
一个更简单的案例:
int main(void)
{
int x;
_Generic(0, int: x = 5, float: x = (void)0);
}
此代码在gcc中提供约束违规,因为它对所有关联的表达式(而不仅仅是选定的表达式)执行约束检查,x = (void)0
包含约束违规。
在没有默认情况下将此原则应用于您的代码,我们发现问题是当使用Y
将int *s
实例化为声明为_Generic(s, double * : test_double)
的变量时,其中一个关联表达式为{ {1}},这是一种约束违规,因为没有案例匹配。
答案 1 :(得分:4)
选择在编译时发生,但这样做不意味着丢弃其他(未选中)代码。它仍然必须有效,这意味着......
如果未使用default且没有任何类型名称与控制表达式的类型兼容,则程序将无法编译。
这是一个令人惊讶的:
没有“first Y”的默认情况:
#define test(X, Y, c) \
_Generic((X), \
double: _Generic((Y), \
double * : test_double \
), \
int: _Generic((Y), \
int * : test_int, \
default: test_default \
) \
) (X, Y, c)
我收到了这个错误:
prog.c:6:30:错误:'int *'类型的'_Generic'选择器与任何关联都不兼容
请注意,它抱怨int *
不兼容!为什么?
那么让我们看看报道的行:
int a2 = test(i, s, 1);
X
类型为int
,Y
类型为int *
。
现在是重要的部分:扩展发生无条件。因此,即使X
属于int
类型,X
的第一个关联(当类型为double
时)也必须是格式良好的程序。因此,如果Y
为int *
,则必须确保以下内容:
_Generic((Y), \
double * : test_double \
), \
由于int *
不是double *
,因此事情会在这里破裂。
我只是在查看标准(实际上是N1570)并且找不到实际上正确指定此行为的任何内容。我想在这种情况下可以报告一个缺陷,标准对此太过模糊。我现在正试图这样做。
答案 2 :(得分:1)
这显然是因为嵌套的_Generic选择。
问题是,使用_Generic时,必须在编译时满足所有可能的泛型关联的类型兼容性。即使只选择了一个关联,但未选择的关联仍必须具有一些兼容的关联。
关联默认值处理与该_Generic选择的其余关联不兼容的每个关联。
假设您删除int: _Generic((Y),
上的默认关联。如果这样做,则选择双关联,但int关联仍然必须处理double *类型,这通常是默认情况下完成的。在这种情况下,只有int *关联,它输出错误。
答案 3 :(得分:1)
是优先顺序。在您的代码中,两个_Generic((Y)
在_Generic((X)
之前运行。它们在括号中。编译器非常渴望并为test(3.4, t, 1)
和test(i, s, 1)
运行。
我会稍微改写一下代码。
#define test(X, Y, c) \
_Generic((X), \
double: _Generic((Y), \
double * : test_double, \
default : test_error \
), \
int: _Generic((Y), \
int * : test_int, \
default : test_error \
) \
) (X, Y, c)
static void test_error(void)
{
/* This function should be optimised away by compiler. */
assert(0);
}