C++ 静态常量成员变量、变量模板和&;的编译器错误或正确行为&;?

C++ 静态常量成员变量、变量模板和&;的编译器错误或正确行为&;?,c++,c++11,rvalue-reference,compiler-bug,C++,C++11,Rvalue Reference,Compiler Bug,我在编译下面包含的代码时注意到一个奇怪的行为。我有4个文件如下 createshared.h: staticinitclass.cpp: 很好,因为默认的_i是一个整数类型,所以我们可以在头中初始化它。让我们这样做 很好,仍然编译并工作良好。现在,让我们添加&&和std::forward 链接器错误。现在,让我们尝试初始化.cpp中的默认_i成员 它又起作用了。使用clang也会产生同样的结果,这让我相信这不仅仅是一个孤立的编译器错误,而且可能是语言本身的某些东西阻止了静态初始化。我似

我在编译下面包含的代码时注意到一个奇怪的行为。我有4个文件如下

createshared.h:

staticinitclass.cpp:

很好,因为默认的_i是一个整数类型,所以我们可以在头中初始化它。让我们这样做

很好,仍然编译并工作良好。现在,让我们添加&&和std::forward

链接器错误。现在,让我们尝试初始化.cpp中的默认_i成员

它又起作用了。使用clang也会产生同样的结果,这让我相信这不仅仅是一个孤立的编译器错误,而且可能是语言本身的某些东西阻止了静态初始化。我似乎无法理解为什么添加&会导致中断

目前我正在Ubuntu 14.04上使用g++4.8.2和clang++3.5


在§9.4.2[类.静态.数据]之后使用-dinInitialize_IN_HEADER和-DUSE_refrefref?

时,这里有什么问题

3如果非易失性常量静态数据成员是整型或枚举型,则其在类定义中的声明可以指定一个大括号或相等的初始值设定项,其中作为赋值表达式的每个初始值设定项子句都是常量表达式(5.19)。[…]如果在程序中使用odr(3.2),则仍应在名称空间范围中定义成员,并且名称空间范围定义不应包含初始值设定项。

换句话说,在头中直接给const静态数据成员一个值并不意味着不需要定义该数据成员。您应该在staticinitclass.cpp文件中包含以下内容:

#ifndef INITIALIZE_IN_HEADER
const int StaticInitClass::default_i = 2;
#else
const int StaticInitClass::default_i; // this is what you don't have
#endif
绑定到引用(在您的情况下,绑定到转发引用
&&
被推断为常量左值引用)算作此数据成员的odr使用


如果您不使用转发引用,并且按值获取参数,则它不是该静态数据成员的odr使用,因此不会引发链接器错误。

为静态数据成员提供初始值设定项并不意味着您还提供了定义。当您将两个
-D
选项组合在一起时,最终导致odr使用该静态成员,因此需要为其提供一个定义。缺少的部分是,仅使用该值不算作odr使用。@t.C.也添加了这个!我完全不知道odr使用的规范。谢谢
#ifndef STATICINITCLASS_H_
#define STATICINITCLASS_H_

class StaticInitClass
{
public:
#ifdef INITIALIZE_IN_HEADER
    static const int default_i = 1;
#else
    static const int default_i;
#endif
    virtual ~StaticInitClass() = default;
    StaticInitClass() = delete;
protected:
    StaticInitClass(int i);
};

#endif
#include "staticinitclass.h"

#include <iostream>

#ifndef INITIALIZE_IN_HEADER
const int StaticInitClass::default_i = 2;
#endif

StaticInitClass::StaticInitClass(int i)
{
    std::cout << "Created with " << i << std::endl;
}
#include "staticinitclass.h"
#include "createshared.h"
#include <memory>

int main(int argc, const char* argv[])
{
    auto shared = create_shared<StaticInitClass>(StaticInitClass::default_i);
}
$ g++ -std=c++11 main.cpp staticinitclass.cpp 
$ ./a.out 
Created with 2
$ g++ -std=c++11 main.cpp staticinitclass.cpp -DINITIALIZE_IN_HEADER
$ ./a.out 
Created with 1
$ g++ -std=c++11 main.cpp staticinitclass.cpp -DINITIALIZE_IN_HEADER -DUSE_REFREF
/tmp/cc3G4tjc.o: In function `main':
main.cpp:(.text+0xaf): undefined reference to `StaticInitClass::default_i'
collect2: error: ld returned 1 exit status
$ g++ -std=c++11 main.cpp staticinitclass.cpp -DUSE_REFREF
$ ./a.out 
Created with 2
#ifndef INITIALIZE_IN_HEADER
const int StaticInitClass::default_i = 2;
#else
const int StaticInitClass::default_i; // this is what you don't have
#endif