Is the standard behavior adding const to size_t, can cause compilation to fail?

I recently read a cool article: https://akrzemi1.wordpress.com/2015/08/20/can-you-see-the-bug/ When playing with the reduced version on ideone, I get amazing behavior:

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

does not compile, but the same program with const compilers:

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

So my question is whether this standard is required, or only the compilers decided that this is a compiler warning, and the other is a compiler error.

If this helps here errors and warnings from gcc 5.1 (tnx godbolt)

!! Error: narrowing the conversion of '258ul' from 'size_t {aka long unsigned int}' to 'char' inside {}


!! warning: narrowing the conversion of 'sz' from 'size_t {aka long unsigned int}' to 'char' inside {} [-Wnarrowing]

the good guy clang 3.6 gives an error in both cases, but the question remains, is one legitimate and bad C ++ and the other illegal C ++?

+6
source share
3 answers

std::string has constructors declared as:

 string::string(std::initializer_list<char>); string::string(std::size_t, char); 

When we have list initialization, the following rule applies:

(N3337 [dcl.init.list]/3): An initialization list of an object or link of type T is defined as follows:

  • [...]
  • Otherwise, if T is a class type, constructors are considered. The constructors used are listed and the best one is selected using overload resolution (13.3, 13.3.1.7). If narrowing the conversion (see below) is required to convert any of the arguments, the program is poorly formed.

The initializer list constructor is selected because of this rule:

(N3337 [over.match.list]/1): When objects of a non-aggregate type T are initialized by a list (8.5.4), the overload resolution is selected by the constructor in two phases:

  • Initially, candidate functions are initializer-list constructors (8.5.4) of class T, and the argument list consists of a list of initializers as a single argument.
  • [...]

Now, since the constructor of the initializer list is the best choice, but a narrowing conversion is required to convert the argument, the program is poorly formed.

However, I do not think that one compiler is correct and one is incorrect:

(N3337 [intro.compliance]/8): corresponding implementation may have extensions (including additional library functions) if they are executed and do not change the behavior of any well-formed program. Implementations are needed to diagnose programs that use extensions that are poorly formed in accordance with this International Standard. However, by doing this, they can compile and execute such programs.

The standard does not have the concept of warning and error, so GCC is compatible with the issuance of diagnostic diagnostics, and then still collects the program. Note that GCC will -pedantic-errors error if you pass -pedantic-errors .

+6
source

It narrows here since the initializer_list<char> constructor is used.

N4296 8.5 / 17.1

- If the initializer is the (not beveled) bracket-init-list, the object or link is initialized with a list (8.5.4).

Since string has a constructor that accepts initializer_list<char> , this will be preferable since

8.5.4 / 2

The constructor is the constructor of the initializer list if its first parameter is of type std :: initializer_list or a link to, possibly, cv-qual std :: initializer_list for some type E, and either there are no other parameters, otherwise all other parameters are arguments by default (8.3 .6). [Note: constructors of the list of initializers preference is given to other designers in the initialization of the list (13.3.1.7).

13.3.1.7/1

When objects of a non-aggregate type T are initialized by a list, such that in 8.5.4 it is indicated that overload resolution is performed in accordance with the rules in this section, the overload resolution is selected by the constructor in two phases

Initially, the candidate functions are a list of initializers (8.5.4) constructors of class T and the list of arguments consists of a list of initializers as one argument.

and the program is poorly formed since

N4296 8.5.4 / 3.5

Otherwise, if T is a class type, constructors are considered. Applicable constructors are listed and selected best 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 poorly formed.

Basically gcc will warn only about this fragment:

 int main() { const size_t v = 258; char c = {v}; } 

when clang will throw an error.

+3
source

The problem is that std::string has an initializer list constructor, and it is greedy. {sz,'#'} processed as a list of initializers, and you get a warning about narrowing the conversion, since it converts sz to char to make std::initializer_list<char> . You can fix this by calling constrcutor with () instead of {}

+3
source

All Articles