2
votes

This code compile fine in Visual and failed in GCC or Clang.

I understand that the conversion from std::byte to char might be a problem. Is it forbidden to provide this conversion and is a visual C++ bug or is it acceptable and a nice feature missing from GCC/Clang?

#include <vector>
#include <string>

int main()
{
    std::vector<std::byte> test(10);
    std::string str(test.begin(), test.end());
}

Edit

Microsoft (fixed it)

We have fixed the problem in a future release. Now MSVC provide an error like below: error C2664: 'void std::basic_string,std::allocator>::push_back(const _Elem)': cannot convert argument 1 from 'std::byte' to 'const _Elem' with [ _Elem=char ]

2
@Someprogrammerdude I think the concern here is the discrepancy between Visual C++ and GCC and Clang.François Andrieux
@Someprogrammerdude std::byte is distinct from the other types you mention in that it's isn't considered a character type and is not considered an integral type.François Andrieux
MS fails to compile this code so they must be doing something special to handle std::byte.NathanOliver
Are you using the /J (default char type is unsigned) compiler option? If I read this right it would allow this to compile.HerrJoebob

2 Answers

2
votes

MSVS's string constructor that takes an iterator pair looks like

template<class _Iter,
    class = enable_if_t<_Is_iterator_v<_Iter>>>
    basic_string(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc())
    : _Mybase(_Al)
    {   // construct from [_First, _Last) with optional allocator
    _Tidy_init();
    _Adl_verify_range(_First, _Last);
    _Construct(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Iter_cat_t<_Iter>());
    }

And _Construct eventualy calls

template<class _Iter>
    void _Construct(_Iter _First, const _Iter _Last, input_iterator_tag)
    {   // initialize from [_First, _Last), input iterators
    _TRY_BEGIN
    for (; _First != _Last; ++_First)
        {
        push_back(static_cast<_Elem>(*_First));
        }

    _CATCH_ALL
    _Tidy_deallocate();
    _RERAISE;
    _CATCH_END
    }

With the important bit being push_back(static_cast<_Elem>(*_First));. Normally you would not be able to assign a std::byte directly to another type since it is a scoped enumeration but since there is a static_cast you get around that. This is why you are able to use a range of bytes to initialize a std::string.

That said the iterator constructor requires from [sequence.reqmts] - Table 68 that

T shall be EmplaceConstructible into X from *i.

and [container.requirements.general]/15.5 states that EmplaceConstructible means

T is EmplaceConstructible into X from args , for zero or more arguments args, means that the following expression is well-formed:

allocator_traits<A>::construct(m, p, args)

and construct is defined in [allocator.requirements] - Table 33 as

Effects: Constructs an object of type C at c

Default ::new ((void*)c)C(forward<Args>(args)...)

So it is still unclear if this is allowed or not.

1
votes

Visual C++ is wrong. std::byte is a scoped enumeration, i.e., declared as if by enum class, and therefore cannot be converted to char or any other arithmetic type unless an explicit cast is used.