关于语言链接,GCC 和 clang 在这种情况下是否显示与 Visual Studio 相同的结果?

Do GCC and clang show the same result as Visual Studio on this case, about language linkage?

[dcl.link]/4:

Linkage specifications nest. When linkage specifications nest, the innermost one determines the language linkage. A linkage specification does not establish a scope. A linkage-specification shall occur only in namespace scope. In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification. [ Example:

extern "C"                      // the name f1 and its function type have C language linkage;
  void f1(void(*pf)(int));      // pf is a pointer to a C function

...

— end example ]

观察下面传递给函数c_f()的指针&foo而不是指向C函数的指针。这段代码在VS2017中编译链接正常。但它不应该,根据 [dcl.link]/4.

文件 main.cpp:

#include <stdio.h>
extern "C"                          // the name c_f and its function type have C language linkage;
void c_f(void(*pf)(int));           // pf is a pointer to a C function

void foo(int i) {
    printf("%d\n", i);
}

extern "C" void c_foo(int);

int main() {
    c_foo(1);       // Calls c_foo(int) defined in other.c
    c_f(&foo);      // Calls c_f(void(*)(int)) defined in other.c, but &foo is not a pointer to a C function !! 
}

文件 other.c:

#include <stdio.h>

void c_f(void(*pf)(int)){
    pf(2);
}

void c_foo(int i) {
    printf("%d\n", i);
}

我很想知道 clang 和 GCC 是否符合标准,但我无法在网络编译器中验证这一点。

编辑

我恍然大悟,在上面提到的问题上,我真的不需要两个文件来验证clang和GCC是否符合标准。如果标准需要 C 函数的地址,作为函数 c_f() 的参数,而 main.cpp 中的代码提供 C++ 函数的地址,C++ 编译器不得不抱怨 1 编译此文件时。但这在 clang nor in GCC 中都不会发生。那么我不妨说clang和GCC在这方面也是有bug的

1) 如果我们假设需要诊断

您的代码根据 [dcl.link]/1 and [expr.call]/1(重点是我的)显示未定义的行为:

[dcl.link]/1:

All function types, function names with external linkage, and variable names with external linkage have a language linkage. [ Note: Some of the properties associated with an entity with language linkage are specific to each implementation and are not described here. For example, a particular language linkage may be associated with a particular form of representing names of objects and functions with external linkage, or with a particular calling convention, etc. — end note ] The default language linkage of all function types, function names, and variable names is C++ language linkage. Two function types with different language linkages are distinct types even if they are otherwise identical.

[expr.call]/1:

A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of initializer-clauses which constitute the arguments to the function. The postfix expression shall have function type or function pointer type. For a call to a non-member function or to a static member function, the postfix expression shall be either an lvalue that refers to a function (in which case the function-to-pointer standard conversion is suppressed on the postfix expression), or it shall have function pointer type. Calling a function through an expression whose function type is different from the function type of the called function's definition results in undefined behavior ([dcl.link]). For a call to a non-static member function, the postfix expression shall be an implicit ([class.mfct.non-static], [class.static]) or explicit class member access whose id-expression is a function member name, or a pointer-to-member expression selecting a function member; the call is as a member of the class object referred to by the object expression. In the case of an implicit class member access, the implied object is the one pointed to by this. [ Note: A member function call of the form f() is interpreted as (*this).f() (see [class.mfct.non-static]). — end note ] If a function or member function name is used, the name can be overloaded, in which case the appropriate function shall be selected according to the rules in [over.match]. If the selected function is non-virtual, or if the id-expression in the class member access expression is a qualified-id, that function is called. Otherwise, its final overrider in the dynamic type of the object expression is called; such a call is referred to as a virtual function call. [ Note: The dynamic type is the type of the object referred to by the current value of the object expression. [class.cdtor] describes the behavior of virtual function calls when the object expression refers to an object under construction or destruction. — end note ]