2015-08-28 53 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

相關問題