'MSVC 19.11 / Visual C++ 2017: initializer list of size 1 and size_t type misinterpreted

Compiling the following code with MSVC 19.11 results in the output

With 32: 0 99 2 With 64: 0 1 2 with a 32bit compiler, and in

With 32: 0 1 2 With 64: 0 99 2 with a 64bit compiler.

The issue is that the single element initializer list is exactly of type size_t. Is this a compiler bug (I have not found it reported anywhere so far) and not a case of the standard being ambiguous (neither clang nor gcc have this problem)?

#include <cstdint>
#include <vector>
#include <iostream>

int main() {
    using T = std::uint16_t;
    // fixed with uint32 / uint64 on 32 / 64 bit compilers, respectively,
    // but not with int32_t / int64_t
    {
        std::vector<T> s0;
        //  std::vector<T> s1{ 99u }; // OK
        //  std::vector<T> s1 = { 99u }; // OK
        std::vector<T> s1( { 99u } ); // BUG?
 // EDIT: std::vector<T> s1( {{ 99u }} ); // also OK
        std::vector<T> s2( { 40u, 70u } );
        std::cout << "With " << sizeof(0u)*8 << ':' << ' '
          << s0.size() << ' ' << s1.size() << ' ' << s2.size() << '\n';
    }

    {
        std::vector<T> s0;
        std::vector<T> s1( { 99ull } );
        std::vector<T> s2( { 40ull, 70ull } );
        std::cout << "With " << sizeof(0ull)*8 << ':' << ' '
          << s0.size() << ' ' << s1.size() << ' ' << s2.size() << '\n';
    }

    return 0;
}

Command and Compiler(s):

cl.exe ilist.cpp & .\ilist.exe   # no extra cl arguments

cl.exe
Microsoft (R) C/C++ Optimizing Compiler Version 19.11.25507.1 for x64
Copyright (C) Microsoft Corporation.  All rights reserved. (or x86)

x64\cl.exe and x86\cl.exe from
...\Tools\MSVC\14.11.25503\bin\HostX64\


Solution 1:[1]

Yes, this is a Visual Studio compiler bug.

A simpler example to show the problem:

#include <initializer_list>
#include <cstddef>

struct V {
    size_t sz;
    constexpr V(size_t s) { sz = s; } 
    constexpr V(std::initializer_list<int> l) { sz = l.size(); }
};

static_assert( V({size_t(3)}).sz == 1 );

Here struct V mimics std::vector with its two constructors in question.

According to over.ics.rank#3.1

List-initialization sequence L1 is a better conversion sequence than list-initialization sequence L2 if (3.1.1) L1 converts to std?::?initializer_­list for some X and L2 does not...

So in this example V({size_t(3)}) shall call the constructor V(std::initializer_list<int> l), and it does so in GCC and Clang, but not in MSVC. Demo: https://gcc.godbolt.org/z/3q64f7Yr7

MSVC bug reported: https://developercommunity.visualstudio.com/t/Wrong-constructor-of-std::vector-selecte/1652923

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Fedor