Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ variant converting constructor with bool

Tags:

c++

c++17

c++20

On cppreference (4) the converting constructor is described as follows:

Converting constructor. Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression F(std::forward<T>(t)) if there was an overload of imaginary function F(T_i) for every T_i from Types... in scope at the same time, except that:

  • An overload F(T_i) is only considered if the declaration T_i x[] = { std::forward<T>(t) }; is valid for some invented variable x;
  • If T_i is (possibly cv-qualified) bool, F(T_i) is only considered if std:remove_cvref_t<T> is also bool.

I am especially interested in the second bullet point regarding the bool. In the example it says, that:

std::variant<std::string, bool> y("abc"); // OK, chooses string; bool is not a candidate

I have now tested the same code with clang 7.0.0 (godbolt), gcc.8.2 (godbolt) and VS2017. And I am wondering why the contained alternative type is bool (for all the three compilers) and not std::string as described by cppreference. Is this a bug in the standard-libraries of all the three compilers?

I also found following document: P0608R3. Does it mean, that the modifications (the two bullet points) that cppreference lists are only proposed but not part of the official standard yet?

like image 677
Kilian Avatar asked Feb 01 '19 15:02

Kilian


2 Answers

P0608R3 was adopted in San Diego. Its wording was applied to the working draft - you can see the new wording in [variant.ctor]/12.

As a part of that change, the motivating example:

variant<string, bool> x = "abc";

Does now hold a string (in c++20), whereas it used to hold a bool (in c++17). The meaning of this example changes between standard versions.

It's just that none of the standard libraries have implemented this change yet. It's very recent. It's listed as incomplete in both the libstdc++ and libc++ pages. But as you can see, there's tons of C++20 features that haven't been implemented yet either. The good news is, it's still early 2019 and there's plenty of time.

like image 186
Barry Avatar answered Nov 19 '22 04:11

Barry


Every version of the C++ standard has bugs. Hundreds of them.

C++ implementations aim to be useful, so they do not slavishly adhere to the published standard text. There's no reason to maintain bug-for-bug compatibility with a piece of paper.

(As an extreme example, until C++17 the standard technically required the <int> in std::vector<int> v; to be parsed as a header-name and then rejected because it is not inside a #include directive. It should go without saying that no compiler will do that.)

Cppreference also aims to be useful. So we do not maintain bug-for-bug compatibility with the standards either. When a piece of text first appeared in a piece of paper published by ISO is not useful (except for standard historians, perhaps); as programmers, what we care about is what we get when we use -std=c++17, or whatever your implementation's equivalent flag is. As a result, our documentation is for a hypothetical complete and correct implementation of each C++ standard plus all subsequent bugfixes and clarifications applicable to that standard.* We use the current implementations as evidence for what such a hypothetical implementation would do.

When there is no current implementation for a particular change, we evaluate the nature of the change to predict how the implementations would handle it. Core language changes intended to be retroactive are labeled as defect reports, which make the call easier (though sometimes they don't reach all the way back, and that is not in the labeling). Library changes do not come with consistently applied "DR" labels, however, so the call is more up to us.

In this particular case, while P0608 is not labeled as a defect report, it corrects an extremely questionable behavior in C++17 soon after its publication. Additionally, it is highly undesirable for code like std::variant<std::string, bool> x = "abcd"; to silently change meaning on the same implementation depending on the standard mode. Code relying on std::variant is also uncommon in the wild (this is partially why the committee even approved the "breaking" change in the first place). As a result, I predicted that the paper will eventually be applied retroactively and documented it accordingly.


*This is a change in philosophy from a few years back; as a result, we still have lots of cases where a bugfix is not treated as retroactive in the documentation but should be. They are being slowly cleaned up over time.

like image 40
T.C. Avatar answered Nov 19 '22 06:11

T.C.