带有大括号的 char 数组的非静态成员初始化在 gcc 中给出了一个错误,而不是在 clang 中
Non-static member initialization of char array with brace gives an error in gcc while not in clang
考虑以下代码:
#include <iostream>
class A
{
char name[40] = { "Blank" }; // note the braces here
public:
const char *getName() { return name; }
};
int main()
{
A a;
std::cout << a.getName() << std::endl;
}
gcc
(最新版本5.2.0
)报错:
prog.cpp:5:28: error: invalid conversion from 'const char*' to 'char' [-fpermissive]
char name[40] = { "Blank" };
^
但 clang
的情况并非如此,用 -std=c++11 -pedantic -Wall
.
可以完美地编译它
在这里为非静态初始化器放大括号真的不正确吗?
AFAIR 有没有大括号并不重要。比如数组的定义,如:
char text[] = "some text";
相当于:
char text[] = { "some text" };
来自 C++ 标准工作草案 n4527 [dcl.init]。初始化可以这样写:
初始化器
initializer:
brace-or-equal-initializer
( expression-list )
brace-or-equal-initializer:
= initializer-clause
braced-init-list
initializer-clause:
assignment-expression
braced-init-list
initializer-list:
initializer-clause...opt
initializer-list,initializer-clause...opt
braced-init-list:
{initializer-list,opt}
{ }
一个Class成员声明
member-declarator:
declarator virt-specifier-seq opt pure-specifier opt
declarator brace-or-equal-initializer opt
根据我的阅读,gcc 似乎不符合标准。因为 class 使用大括号的成员初始化被标准接受。
fwiw,g++
在基于大括号的初始化 and/or 构造函数调用方面存在一些问题,包括最近报告的一些问题。
但是(编辑),正如 Jonathan 正确指出的那样,其中大部分(全部?)都可以轻松解决。我已经成功地将旧的括号语法替换为受影响的代码位。
听到无法做到这一点的情况会很有教育意义,但谢天谢地,我个人还没有找到,所以 g++
对我来说仍然非常有用(评论解释了我为什么使用在某些奇怪的地方使用旧语法!)
嗯,我认为标准不是很清楚,但我会说 CLang 是正确的:
8.5.1 说:
§2 : 当聚合由初始化列表初始化时,如 8.5.4 中所述,初始化列表的元素
被视为聚合成员的初始值设定项,以递增的下标或成员顺序。 那是 gcc 解释:名称是一个数组,有一个大括号初始化列表,所以数组的第一个元素(一个 char ) 使用 char 数组初始化 => error
但 8.5.2 明确指出:窄字符类型的数组...可以由窄字符串文字初始化...或由适当类型的字符串文字初始化用大括号括起来(强调我的)
我的解释是,该标准认为 char 数组足够特殊,可以明确允许括在大括号中的字符串文字有效,即使它违反了 8.5.1 §2
该代码有效,现在已被 GCC 主干接受。我认为它已被 PR 65815 brace elision doesn't work in NSDMI
修复
考虑以下代码:
#include <iostream>
class A
{
char name[40] = { "Blank" }; // note the braces here
public:
const char *getName() { return name; }
};
int main()
{
A a;
std::cout << a.getName() << std::endl;
}
gcc
(最新版本5.2.0
)报错:
prog.cpp:5:28: error: invalid conversion from 'const char*' to 'char' [-fpermissive]
char name[40] = { "Blank" };
^
但 clang
的情况并非如此,用 -std=c++11 -pedantic -Wall
.
在这里为非静态初始化器放大括号真的不正确吗?
AFAIR 有没有大括号并不重要。比如数组的定义,如:
char text[] = "some text";
相当于:
char text[] = { "some text" };
来自 C++ 标准工作草案 n4527 [dcl.init]。初始化可以这样写:
初始化器
initializer: brace-or-equal-initializer ( expression-list ) brace-or-equal-initializer: = initializer-clause braced-init-list initializer-clause: assignment-expression braced-init-list initializer-list: initializer-clause...opt initializer-list,initializer-clause...opt braced-init-list: {initializer-list,opt} { }
一个Class成员声明
member-declarator: declarator virt-specifier-seq opt pure-specifier opt declarator brace-or-equal-initializer opt
根据我的阅读,gcc 似乎不符合标准。因为 class 使用大括号的成员初始化被标准接受。
fwiw,g++
在基于大括号的初始化 and/or 构造函数调用方面存在一些问题,包括最近报告的一些问题。
但是(编辑),正如 Jonathan 正确指出的那样,其中大部分(全部?)都可以轻松解决。我已经成功地将旧的括号语法替换为受影响的代码位。
听到无法做到这一点的情况会很有教育意义,但谢天谢地,我个人还没有找到,所以 g++
对我来说仍然非常有用(评论解释了我为什么使用在某些奇怪的地方使用旧语法!)
嗯,我认为标准不是很清楚,但我会说 CLang 是正确的:
8.5.1 说:
§2 : 当聚合由初始化列表初始化时,如 8.5.4 中所述,初始化列表的元素 被视为聚合成员的初始值设定项,以递增的下标或成员顺序。 那是 gcc 解释:名称是一个数组,有一个大括号初始化列表,所以数组的第一个元素(一个 char ) 使用 char 数组初始化 => error
但 8.5.2 明确指出:窄字符类型的数组...可以由窄字符串文字初始化...或由适当类型的字符串文字初始化用大括号括起来(强调我的)
我的解释是,该标准认为 char 数组足够特殊,可以明确允许括在大括号中的字符串文字有效,即使它违反了 8.5.1 §2
该代码有效,现在已被 GCC 主干接受。我认为它已被 PR 65815 brace elision doesn't work in NSDMI
修复