Подтвердить что ты не робот

Различные результаты в Clang и GCC при литье в std:: optional <T>

С учетом следующего кода:

#include <iostream>
#include <optional>

struct foo
{
    explicit operator std::optional<int>() {
        return std::optional<int>( 1 );
    }
    explicit operator int() {
        return 0;
    }
};

int main()
{
    foo my_foo;
    std::optional<int> my_opt( my_foo );
    std::cout << "value: " << my_opt.value() << std::endl;
}

gcc 7.2.0 пишет value: 1.

MSVC 2017 (15.3) и clang 4.0.0, но пишите value: 0.

Какой из них правильный в соответствии со стандартом С++?

4b9b3361

Ответ 1

Так как это прямая инициализация, мы перечисляем конструкторы и просто выбираем лучший. Соответствующие конструкторы для std::optional:

constexpr optional( const optional& other ); // (2)
constexpr optional( optional&& other ) noexcept(/* see below */); // (3)

template < class U = value_type >
/* EXPLICIT */ constexpr optional( U&& value ); // (8), with U = foo&

Оба являются жизнеспособными ((8) участвует только в разрешении перегрузки, если int является конструктивным из foo& и foo не является ни std::in_place_t, ни std::optional<int>, все из которых сохраняются), но (8) является точное соответствие, тогда как (2) и (3) требуют пользовательского преобразования, поэтому это должно быть предпочтительным. gcc здесь не так.

Однако gcc фактически не вызывает (3). Он просто непосредственно инициализирует my_opt из результата преобразования my_foo в optional<int>. Эта программа с gcc 7.2 печатает 3, но ни один из 1a, 1b или 2:

#include <iostream>

template <class T>
struct opt {
    opt() { }
    opt(opt const& ) { std::cout << "1a\n"; }
    opt(opt&& ) { std::cout << "1b\n"; }

    template <class U>
    opt(U&& ) { std::cout << "2\n"; }
};

struct foo 
{
    explicit operator opt<int>() { std::cout << "3\n"; return {}; }
};

int main()
{
    opt<int> o(foo{});
}

Я не думаю, что это допустимый маршрут. Я подал 81952.