2015-08-28 84 views
6

我最近看了酷文章: https://akrzemi1.wordpress.com/2015/08/20/can-you-see-the-bug/ 降低版本上ideone打我得到了令人惊讶的行为:将const添加到size_t是否会导致编译失败的标准行为?

#include <iostream> 
#include <cassert> 
using namespace std; 
int main() { 
    const size_t sz=258; 
    string s{sz,'#'}; 
    assert(2==s.size()); 
} 

不能编译,但 与常量同一程序中删除编译:

#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]

好人铛3.6给出了在这两种情况下的错误,但仍然是一个问题,是一个合法和不良C++和其他非法C++?

+3

请注意,该标准只需要诊断。它不区分错误和警告。在这个错误报告中讨论了这个问题:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55783 –

+0

根本问题是'string s {32,32,32};'应该工作。但'32'是一个int,而int-char是一个缩小的转换。我们知道'32'可以安全地缩小,正是因为它是编译时常量。由于这个原因,编译时常量的处理方式不同。 (它不只是'const' - 'const sz = rand()'不是一个编译时常量,在缩小时可能会溢出) – MSalters

回答

6

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 发出错误。

+0

都是你的,ForEveR答案很好,但我接受了你的最后一段。 – NoSenseEtAl

3

这里缩小了,因为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}; 
} 

时铛会给错误。

+0

但是缩小转换在哪里? 'std :: string'有一个构造函数,它需要'size_t'和'char',所以这些是完全匹配的。 – nwp

+4

@nwp'std :: initializer_list'构造函数是贪婪的,所以它调用'string :: string(std :: initializer_list )'而不是'string :: string(std :: size_t,char)'。 – TartanLlama

+0

@TartanLlama:注释本身可能是非规范性的,但是形式为“[注:见规则X(13.3.1.7)]”的注释肯定可以引用标准的规范部分。 – MSalters

3

问题是std::string有一个初始化列表构造函数,它是贪婪的。 {sz,'#'}正在作为初始化程序列表进行处理,并且由于它将sz转换为字符类型以生成std::initializer_list<char>,因此您将收到缩小的转换警告。您可以通过调用constrcutor与{}()代替

+0

我知道,但这不是问题,我的问题是,如果const使程序不合格或不合格,请参阅TartanLlama答案。 – NoSenseEtAl

相关问题