将 const 添加到 size_t 会导致编译失败是标准行为吗?
Is it standard behaviour that adding const to size_t can cause compile failure?
我最近读了一篇很酷的文章:
https://akrzemi1.wordpress.com/2015/08/20/can-you-see-the-bug/
在 ideone 上玩简化版时我得到了令人惊讶的行为:
#include <iostream>
#include <cassert>
using namespace std;
int main() {
const size_t sz=258;
string s{sz,'#'};
assert(2==s.size());
}
不编译,但是
删除 const 的同一程序编译:
#include <iostream>
#include <cassert>
using namespace std;
int main() {
size_t sz=258;
string s{sz,'#'};
assert(2==s.size());
}
所以我的问题是这个标准是必需的,或者只是编译器决定一个是编译器警告,另一个是编译器错误。
如果有帮助,请参考 gcc 5.1 (tnx godbolt) 的错误和警告
!!error: narrowing conversion of '258ul' from 'size_t {aka long
unsigned int}' to 'char' inside { }
!!warning: narrowing conversion of 'sz' from 'size_t {aka long
unsigned int}' to 'char' inside { } [-Wnarrowing]
good guy clang 3.6 在这两种情况下都给出了错误,但问题仍然存在,是一个合法的错误的 C++ 还是另一个非法的 C++?
这里缩小了,因为 initializer_list<char>
构造函数是首选。
N4296 8.5/17.1
— If the initializer is a (non-parenthesized) braced-init-list, the
object or reference is list-initialized (8.5.4).
因为 string
有构造函数,需要 initializer_list<char>
它将被优先考虑,因为
8.5.4/2
A constructor is an initializer-list constructor if its first
parameter is of type std::initializer_list or reference to possibly
cv-qualified std::initializer_list for some type E, and either
there are no other parameters or else all other parameters have
default arguments (8.3.6). [ Note: Initializer-list constructors are
favored over other constructors in list-initialization (13.3.1.7).
13.3.1.7/1
When objects of non-aggregate class type T are list-initialized such
that 8.5.4 specifies that overload resolution is performed according
to the rules in this section, overload resolution selects the
constructor in two phases
Initially, the candidate functions are the initializer-list
constructors (8.5.4) of the class T and the argument list consists of
the initializer list as a single argument.
并且程序格式错误,因为
N4296 8.5.4/3.5
Otherwise, if T is a class type, constructors are considered. The
applicable constructors are enumerated and the best one is chosen
through overload resolution (13.3, 13.3.1.7). If a narrowing
conversion (see below) is required to convert any of the arguments,
the program is ill-formed.
基本上 gcc 只会对这个片段发出警告:
int main() {
const size_t v = 258;
char c = {v};
}
什么时候clang报错
问题是 std::string
有一个初始化列表构造函数,它是贪婪的。 {sz,'#'}
被视为初始值设定项列表,并且您在将 sz
转换为 char 类型以生成 std::initializer_list<char>
时收到缩小转换警告。您可以通过使用 ()
而不是 {}
调用构造函数来解决此问题
std::string
的构造函数声明为:
string::string(std::initializer_list<char>);
string::string(std::size_t, char);
当我们进行列表初始化时,以下规则适用:
(N3337 [dcl.init.list]/3):
List-initialization of an object or reference of type T is defined as follows:
- [...]
- Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated
and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see
below) is required to convert any of the arguments, the program is ill-formed.
由于这条规则选择了初始化列表构造函数:
(N3337 [over.match.list]/1):
When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
- Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the
argument list consists of the initializer list as a single argument.
- [...]
既然初始化列表构造函数是最佳选择,但转换参数需要缩小转换,程序格式不正确。
但是,我不认为这会使编译器一种正确而另一种不正确:
(N3337 [intro.compliance]/8):
A conforming implementation may have extensions (including additional library functions), provided they do
not alter the behavior of any well-formed program. Implementations are required to diagnose programs that
use such extensions that are ill-formed according to this International Standard. Having done so, however,
they can compile and execute such programs.
该标准没有警告与错误的概念,因此 GCC 在发出警告诊断时符合要求,然后继续编译程序。请注意,如果您传递 -pedantic-errors
.
,GCC 将 发出错误
我最近读了一篇很酷的文章: https://akrzemi1.wordpress.com/2015/08/20/can-you-see-the-bug/ 在 ideone 上玩简化版时我得到了令人惊讶的行为:
#include <iostream>
#include <cassert>
using namespace std;
int main() {
const size_t sz=258;
string s{sz,'#'};
assert(2==s.size());
}
不编译,但是 删除 const 的同一程序编译:
#include <iostream>
#include <cassert>
using namespace std;
int main() {
size_t sz=258;
string s{sz,'#'};
assert(2==s.size());
}
所以我的问题是这个标准是必需的,或者只是编译器决定一个是编译器警告,另一个是编译器错误。
如果有帮助,请参考 gcc 5.1 (tnx godbolt) 的错误和警告
!!error: narrowing conversion of '258ul' from 'size_t {aka long unsigned int}' to 'char' inside { }
!!warning: narrowing conversion of 'sz' from 'size_t {aka long unsigned int}' to 'char' inside { } [-Wnarrowing]
good guy clang 3.6 在这两种情况下都给出了错误,但问题仍然存在,是一个合法的错误的 C++ 还是另一个非法的 C++?
这里缩小了,因为 initializer_list<char>
构造函数是首选。
N4296 8.5/17.1
— If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
因为 string
有构造函数,需要 initializer_list<char>
它将被优先考虑,因为
8.5.4/2
A constructor is an initializer-list constructor if its first parameter is of type std::initializer_list or reference to possibly cv-qualified std::initializer_list for some type E, and either there are no other parameters or else all other parameters have default arguments (8.3.6). [ Note: Initializer-list constructors are favored over other constructors in list-initialization (13.3.1.7).
13.3.1.7/1
When objects of non-aggregate class type T are list-initialized such that 8.5.4 specifies that overload resolution is performed according to the rules in this section, overload resolution selects the constructor in two phases
Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
并且程序格式错误,因为
N4296 8.5.4/3.5
Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
基本上 gcc 只会对这个片段发出警告:
int main() {
const size_t v = 258;
char c = {v};
}
什么时候clang报错
问题是 std::string
有一个初始化列表构造函数,它是贪婪的。 {sz,'#'}
被视为初始值设定项列表,并且您在将 sz
转换为 char 类型以生成 std::initializer_list<char>
时收到缩小转换警告。您可以通过使用 ()
而不是 {}
std::string
的构造函数声明为:
string::string(std::initializer_list<char>);
string::string(std::size_t, char);
当我们进行列表初始化时,以下规则适用:
(N3337 [dcl.init.list]/3):
List-initialization of an object or reference of type T is defined as follows:
- [...]
- Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed.
由于这条规则选择了初始化列表构造函数:
(N3337 [over.match.list]/1):
When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
- Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
- [...]
既然初始化列表构造函数是最佳选择,但转换参数需要缩小转换,程序格式不正确。
但是,我不认为这会使编译器一种正确而另一种不正确:
(N3337 [intro.compliance]/8):
A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.
该标准没有警告与错误的概念,因此 GCC 在发出警告诊断时符合要求,然后继续编译程序。请注意,如果您传递 -pedantic-errors
.