MSVC 19.28 отвергает следующий код, но gcc 10.2 принимает его и выводит true false

#include 
#include 

int main()
{
    std::variant v{ 0 };
    std::cout << std::boolalpha << std::holds_alternative(v) << ' ' << std::holds_alternative(v) << std::endl;
}

Согласно cppreference:

  1. Конструктор преобразования. Конструирует вариант, содержащий альтернативный тип T_j, который будет выбран при разрешении перегрузки для выражения F(std::forward(t)), если бы существовала перегрузка воображаемой функции F(T_i) для каждого T_i из Types... в области видимости в одно и то же время. одновременно, за исключением этого: Перегрузка F(T_i) рассматривается только если объявление T_i x[] = { std::forward(t) }; действительно для некоторой придуманной переменной x; Прямая инициализация содержащегося значения как при прямой не списочной инициализации из std::forward(t).

И вопрос преобразуется в то, какая функция из F(long long) и F(double) выбирается против аргумента 1 разрешением перегрузки.

Преобразование int в long long является интегральным преобразованием (предполагается, что sizeof(long long) больше, чем sizeof(int)), а преобразование int в double является плавающим интегральным преобразованием, причем ни одно из них не превосходит другое. Таким образом, вызов неоднозначен, а программа плохо сформирована.

MSVC отклонил код, как я и ожидал, но, к моему удивлению, gcc принял его. Кроме того, есть похожий пример на cppreference:

std::variant v("abc"); // OK
std::variant w("abc"); // ill-formed
std::variant x("abc"); // OK, chooses const char*
std::variant y("abc"); // OK, chooses string; bool is not a candidate
/* THIS ONE -> */ std::variant z = 0; // OK, holds long
                                         // float and double are not candidates 

Так что мой вопрос: является ли gcc или MSVC несоответствием, или мое понимание неверно?

方圆圆

Ответов: 1

Ответы (1)

В процитированном правиле перегрузка рассматривается только в том случае, если копирование-списка-инициализации для типа-кандидата работает от типа аргумента. Эта проверка не учитывает (не может учитывать) статус константного выражения аргумента, поэтому int в любой тип с плавающей точкой является сужающим преобразованием и запрещается списком-инициализацией (несмотря на то, что в типичных реализациях double может точно представлять любое значение int). Поэтому GCC (i.e., libstdc++) правильно игнорирует альтернативу double.

2022 WebDevInsider