在C ++ 11中,引入了一个新功能,程序员可以在类的定义中初始化类成员变量,参见下面的代码:
struct foo
{
int size = 3;
int id = 1;
int type = 2;
unsigned char data[3] = {'1', '2', '3'};
};
这个初始化是在编译期间发生的还是这个特性只是语法糖,而成员变量是在默认构造函数中初始化的?
答案 0 :(得分:27)
首先是,如前所述,它是语法糖。但由于规则可能太难记住,这是一个逻辑实验来帮助你弄清楚在编译时发生了什么以及什么不是
您的c ++ 11类具有类初始值设定项
struct foo { int size = 3; };
另一个可以帮助我们实验的课程
template<int N>
struct experiment { enum { val = N }; };
让我们的假设 H0 是初始化确实发生在编译时,然后我们可以写
foo a;
experiment<a.size> b;
没有运气,我们无法编译。有人可能认为失败是由于foo::size
不稳定所以让我们试试
struct foo { const int size = 3; }; // constexpr instead of const would fail as well
再次,gcc通知我们
'a'的值不能用于常量表达式
实验b;
或(更清楚地)visual studio 2013告诉我们
错误C2975:'N':'example'的模板参数无效,是预期的编译时常量表达式
因此,我们必须丢弃 H0 并推断初始化不会在编译时发生。
有一种旧语法可以解决这个问题
struct foo { static const int size = 3; };
Now this compiles但要注意这是(在技术上和逻辑上)不再在类初始化中。
我不得不撒谎以表达观点,但现在揭露全部真相:消息错误意味着a
是真正的问题。你看,因为你有一个对象的实例(Daniel Frey也提到了这个),所以必须初始化内存(对于成员)(在运行时)。如果成员是(const
)static
,就像在最后一个例子中那样,那么它不是(ny)类的子对象的一部分,你可以在编译时进行初始化。
答案 1 :(得分:3)
成员变量的类内初始化器是用于在构造函数初始化列表中编写它们的语法糖,除非已经存在显式初始化器,在这种情况下它们将被忽略。
静态const成员的类内初始化程序用于常量文字,仍然需要定义(尽管没有初始化程序)。
C ++具有来自C的“似乎”规则,因此允许产生规定观察行为的任何内容 具体来说,这意味着静态对象可以在编译时初始化。
答案 2 :(得分:2)
这只是语法糖。还要考虑实例通常意味着必须使用正确的值初始化的内存。仅仅因为这些值提供了不同的语法并不会改变内存需要初始化的事实 - 这在运行时发生。
答案 3 :(得分:1)
它基本上是用户提供的构造函数的语法糖,用于初始化值。您正在为数据成员提供默认值。当您在编译时或运行时询问是否发生这种情况时,答案取决于其使用的上下文。
希望这些例子会有所帮助。在http://gcc.godbolt.org中尝试它们,看看反汇编和编译错误。
struct S { int size = 3; };
//s's data members are compile time constants
constexpr S s = {};
//r's data members are run time constants
const S r = {};
//rr's data members are run time constants,
//but we don't know the values in this translation unit
extern const S rr;
template <int X> class Foo {};
//Ok, s.size is a compile time expression
Foo<s.size> f;
//Error, r.size is not a compile time expression
Foo<r.size> g;
//Compile time expression, this is same as return 3;
int foo() { return s.size; }
//This also works
constexpr int cfoo() { return s.size; }
//Compiler will optimize this to return 3; because r.size is const.
int bar() { return r.size; }
//Compiler cannot optimize, because we don't know the value of rr.size
//This will have to read the value of rr.size from memory.
int baz() { return rr.size; }
正如其他人所表明的那样,原始类型(如int和float)的静态数据成员(以及全局变量,本质上是相同的)有一些奇怪的规则,它们可以是const但仍然可以在编译时上下文中使用,就像它们是constexpr一样。这是为了向后兼容C和过去缺少constexpr功能。现在它很不幸,因为它只是让理解constexpr以及运行时表达式与编译时表达式的区别更加令人困惑。
答案 4 :(得分:0)
让int size = 3;
完全等同于拥有int size;
,然后每个构造函数在其初始化列表中都没有size
(包括编译器生成的构造函数)size(3)
1}}那里。
严格来说,C ++并没有区分&#34;编译时&#34;和&#34;运行时&#34;。