C++ MSVC是否错误地处理了类作用域静态与整型常量初始值设定项的链接?

C++ MSVC是否错误地处理了类作用域静态与整型常量初始值设定项的链接?,c++,visual-c++,C++,Visual C++,使用C++03: 在foo.h中: class Foo { public: // Declare integral static constant with an initializer static const int some_constant = 42; }; 在foo.cc中: // Define and reserve storage for Foo::some_constant const int Foo::some_constant; 在

使用C++03:

在foo.h中:

class Foo {
    public:
        // Declare integral static constant with an initializer
        static const int some_constant = 42;
};
在foo.cc中:

// Define and reserve storage for Foo::some_constant
const int Foo::some_constant;
在bar.cc中:

#include <foo.h>
// stuff that uses Foo::some_constant;
#包括
//使用Foo::some_常量的东西;
长期以来,我一直认为上述方法是正确的。如果类作用域静态为整型且初始值设定项为常量表达式,则允许其具有初始值设定项。类似地,您必须始终在一个翻译单元中声明静态文件的存储(mod templates,但我们忽略它)

然而,MSVC 2010、2012和2013 RC都未能链接同时包含foo.cc和bar.cc的程序,声称foo::some_常量是乘法定义的

我是否误解了某些事情并做了错事,或者VC在这里出错了?如果是后者,是否有人引用过bug报告或类似的东西


请不要告诉我将初始值设定项移到.cpp文件中。我知道我可以做到这一点,但现在我更感兴趣的是从语言规则和编译器实现质量的角度来看,而不是解决办法。

如果使用初始值设定项初始化静态常量成员,则不需要在类外声明。例如:

namespace std
{
    template<typename T , T val>
    struct integral_constant
    {
        typedef T value_type;
        static const T value = val;
    };
}
名称空间std
{
模板
结构积分常数
{
类型定义T值_类型;
静态常数T值=val;
};
}

将初始化从声明移动到定义,即从foo.h移动到foo.cc

foo.cc:

// Define and reserve storage for Foo::some_constant
const int Foo::some_constant = 42;
如果foo.cc和bar.cc
#都包含“foo.h”
,编译器会看到初始化两次,并抱怨多个定义。foo.h应该是

class Foo {
public:
    // Declare integral static constant here, initialize outside
    static const int some_constant;
};

你是对的;你写它的方式是正确的,而VS是错误的

但是,VS可能会出错,因为您没有禁用编译器扩展。如果您使用
/Za
标志编译,那么它应该可以正常工作

或者,如果块:

#if !_MSC_EXTENSIONS
// Define and reserve storage for Foo::some_constant
const int Foo::some_constant;
#endif

MSVC基于C++标准是不正确的。当包含在其他CPP中时,头文件中的静态成员

some_常量
不是定义。所以不应该有多个符号定义。以下是C++标准(强调矿山)的引用。 声明是定义,除非它声明的函数没有 指定函数体(8.4),它包含外部规范 (7.1.1)或链接规范25(7.5),且均不是初始值设定项 也不是函数体,它在类中声明静态数据成员 定义(9.2,9.4),它是一个类名声明(9.1),它是一个 不透明枚举声明(7.2),它是一个模板参数(14.1),它 是函数声明程序中的参数声明(8.3.5),该函数声明程序 不是函数定义的声明符,或者它是类型定义 声明(7.1.3)、别名声明(7.1.3)、使用声明 (7.3.3)、静态声明(第7条)、属性- 声明(第7条)、空声明(第7条)或 使用指令(7.3.4)


其实没那么简单。如果您获取该成员的地址,它必须有一个定义。@PeteBecker,它不能像处理内联函数的地址一样处理它吗?@PeteBecker True,但是如果您正在编写库,您无法真正知道依赖的代码体是否会获取静态常量的地址,所以声明存储几乎是必需的,对吗?@MarkRansom-假设可以,但规则是定义是必需的。@acm-它取决于库的文档接口是什么。如果它说这是一个常数,你不能接受它的地址,也不能通过引用传递它,那么你就不需要定义。正如我明确指出的,我不想把它移到.cpp文件中。我现在同意,VC女士的行为似乎不尊重第5段(ISO 14882:2003)中所述的ODR豁免。我刚知道这些。很抱歉没有完全阅读你的问题。很有趣。您是否有任何文档的指针来解释导致这种行为的扩展,以及为什么它被认为是“好的”?@acm我认为它不是一个好的扩展。我认为这主要是出于遗留原因(与VS允许将临时变量绑定到非常量左值引用的原因相同)。