Ответ 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.