来自 MSVC 外部“C”的故事

Che*_*tor 5 c c++ visual-c++

[这个问题有一个重复的问题,我可以找到,但这个答案是完全错误的,请参阅下面的 C 代码。]

据我了解,extern "C"C++ 中不会生成 C 代码。它只是一个链接指令。

我有一些这样的extern "C"故事要讲,但今天有一个让我困扰的故事。这是一个完全最新的VS2019,代码如下:

#include <stdint.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
 #endif

// NOTE: in here it is still C++ code, 
// extern "C" is a linkage directive

typedef struct Test Test;

struct Test { 

/* remove this const and MSVC makes no warning 
   leave it in and MSVC complains, a lot
   GCC or clang could not care less
*/
    const  
        uint32_t x; 
} ;

/*
MSVC throws warning C4190:  'make_Test' has C-linkage specified, 
   but returns UDT 'Test' which is incompatible with C
:  see declaration of 'Test'
*/
inline constexpr Test make_Test(uint32_t x_ )
{
    return Test{ x_ };
}

#ifdef __cplusplus
  }
#endif

int main( void )
{
    constexpr auto test_{ make_Test(42) };
    return test_.x ;
}
Run Code Online (Sandbox Code Playgroud)

强制性 GODBOLT 链接:https://godbolt.org/z/ecdz1vqhq

关于该 const 的评论是我问题的要点。

MSVC extern "C" 很大程度上(完全?)没有文档记录。因此,我无法判断我是否违反了这个无证区域的一些规则。许多人声称这是某种“未完全实现”的 C11。

AFAIK 对于 C11(或任何其他 C)结构成员类型具有该 const 是非常好的。古老的海湾合作委员会当然不会在意。正如所见,GODBOLT 在线。

这只是VS2019的一个bug,还是我犯了一个bug?

更新

即使我将 的实现make_Test移至单独的 C 文件中,并将其显式编译为 C,此警告也将保持不变。

关于之前同一个问题“答案” 。C 可以有 const 结构数据成员,当然,C 结构可以在创建时进行列表初始化。请参阅下面的代码:

// gcc prog.c -Wall -Wextra -std=gnu11 "-Wno-unused-parameter" "-Wno-unused-variable"

#include <stdlib.h>

typedef struct Test { const long x; } Test;

static struct Test make_Test(long x)
{
    struct Test  test_ = { x } ;
    return test_;
 }
 int main(const int argc, const char * argv[])
 {
  struct Test test_ = make_Test(42) ;
   return 42;
 }
Run Code Online (Sandbox Code Playgroud)

n. *_* m. 2

C++ 标准中不要求 C++ 实现与 C 实现成对出现。

C++ 标准中不要求所有在 C 和 C++ 中具有相似含义的构造在 C++ 实现与所有或部分 C 实现之间应该是二进制兼容的。甚至没有任何地方要求同一平台上的两个 C 实现应该兼容。

举一个非常简单的例子,一个人可以使用 实现(C 或 C++) ,sizeof(long) == 4并在同一平台(C 或 C++)上使用另一个实现sizeof(long) == 8,这绝对没有任何问题。

回到问题中的特定构造,任何特定的地方都没有要求,虽然struct在 C 和 C++ 中都是完全合法的,但在特定的 C 实现和特定的 C++ 实现中具有相同的布局或相同的参数传递约定。

extern "C"帮助程序员生成与 C 互操作的代码,但该标准不能保证任何特定的构造都能工作,因为 C++ 标准不管理 C 实现。

TL;DR 这不是不符合任何标准的情况,这是特定 C++ 实现和特定 C 实现之间不幸但完全合法的不兼容。由于它们都来自同一生产商,因此它们知道不兼容性,并且您会得到一个很好的警告。