10

Consider the following code:

void fnc(int)
{
    std::cout << "int";
}

void fnc(long double)
{
    std::cout << "long double";
}

int main()
{
    fnc(42.3); // error
}

It gives an error because of an ambiguous call to fnc.
However, if we write the next code:

std::variant<int, long double> v{42.3};
std::cout << v.index();

the output is 1, which demonstrates that the double->long double conversion has been chosen. As far as I know, std::variant follows the C++ rules about conversion ranks, but this example shows the difference. Is there any explanation for such a behavior?

9
  • 1
    Which compiler? Commented Apr 12, 2022 at 11:11
  • 2
    Note that there is a condition about validity of imaginary variable definition T_i x[] = { std::forward<T>(t) }; (en.cppreference.com/w/cpp/utility/variant/variant). This cannot be used for int since narrowing conversion is not allowed with list-initialization. So, the int variant is not considered for overloading. Here is a link to the Standard: eel.is/c++draft/variant.ctor#14.sentence-1. Commented Apr 12, 2022 at 11:13
  • 1
    MSVC does not accept this initialization of the variant. I had to suffix the number with "L" to make it a long double: std::variant<int, long double> v{ 42.3L }; Commented Apr 12, 2022 at 11:17
  • 1
    @Denis I referred to the list initialization of that imaginary variable x. Commented Apr 12, 2022 at 11:17
  • 1
    std::variant has a list of defect reports regarding conversion. Possibly one of those covers this situation. (If so, the compiler I'm using doesn't have the retroactive fix.) Commented Apr 12, 2022 at 11:28

1 Answer 1

6

Before P0608, variant<int, long double> v{42.3} also has the ambiguous issue since 42.3 can be converted to int or long double.

P0608 changed the behavior of variant's constructors:

template<class T> constexpr variant(T&& t) noexcept(see below);

  • Let Tj be a type that is determined as follows: build an imaginary function FUN(Ti) for each alternative type Ti for which Ti x[] = {std::forward<T>(t)}; is well-formed for some invented variable x and, if Ti is cv bool, remove_cvref_t<T> is bool. The overload FUN(Ti) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative Tj which is the type of the contained value after construction.

In your example, the variant has two alternative types: int and long double, so we can build the following expression

        int x[] = {std::forward<double>(42.3)}; // #1
long double y[] = {std::forward<double>(42.3)}; // #2

Since only #2 is well-formed, the variant successfully deduces the type of the contained value type long double.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.