Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it standard behaviour that adding const to size_t can cause compile failure?

I recently read cool article: https://akrzemi1.wordpress.com/2015/08/20/can-you-see-the-bug/ Playing with reduced version on ideone I got surprising behaviour:

#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 same program with const removed compiles:

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

So my question is this standard required or just compilers decided that one is a compiler warning and other is an compiler error.

If it helps here are the errors and warnings from 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 gives error in both cases, but the question remains, is one legal and bad C++ and other illegal C++?

like image 998
NoSenseEtAl Avatar asked Aug 28 '15 11:08

NoSenseEtAl


2 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): 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.

The initializer-list constructor is selected due to this rule:

(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.
  • [...]

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

However, I don't think that makes one compiler correct and one incorrect:

(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.

The standard has no concept of a warning vs. an error, so GCC is conformant in issuing a warning diagnostic, then going on to compile the program anyway. Note that GCC will issue an error if you pass -pedantic-errors.

like image 114
TartanLlama Avatar answered Sep 17 '22 18:09

TartanLlama


The problem is the std::string has an initializer lists constructor and it is greedy. {sz,'#'} is being treaated as an initializer list and you are getting a narrowing conversion warning as it is converting sz to a char type to make an std::initializer_list<char>. You can fixe this by calling the constrcutor with () instead of {}

like image 30
NathanOliver Avatar answered Sep 21 '22 18:09

NathanOliver