Если я удалю конструктор копирования и/или конструктор копирования-присваивания типа Bar,
struct Bar {
Bar() = default;
Bar(Bar const&) = delete;
};
std::optional<Bar> не подлежит копированию.
using T = std::optional<Bar>;
static_assert(!std::is_copy_assignable_v<T>);
Это то, чего я ожидал. Кажется, то же самое верно и для boost::optional<Bar>, но, к сожалению, std::is_copy_assignable_v оценивается как истина.
using T = boost::optional<Bar>;
static_assert(std::is_copy_assignable_v<T>); // why doesn't this fail?
Как следствие, следующий код не компилируется:
template <typename T>
void foo() {
if constexpr (std::is_copy_assignable_v<T>){
T lhs;
T rhs;
lhs = rhs;
} else {
std::cout<< "Nope!\n";
}
}
int main()
{
foo<std::optional<Bar>>(); // works, prints "Nope!"
foo<boost::optional<Bar>>(); // compiler error
}
Ошибка компилятора с GCC 13.2:
In file included from /opt/compiler-explorer/libs/boost_1_84_0/boost/optional.hpp:15,
from <source>:4:
/opt/compiler-explorer/libs/boost_1_84_0/boost/optional/optional.hpp: In instantiation of 'void boost::optional_detail::optional_base<T>::construct(argument_type) [with T = Bar; argument_type = const Bar&]':
/opt/compiler-explorer/libs/boost_1_84_0/boost/optional/optional.hpp:277:20: required from 'void boost::optional_detail::optional_base<T>::assign(const boost::optional_detail::optional_base<T>&) [with T = Bar]'
/opt/compiler-explorer/libs/boost_1_84_0/boost/optional/optional.hpp:249:19: required from 'boost::optional_detail::optional_base<T>& boost::optional_detail::optional_base<T>::operator=(const boost::optional_detail::optional_base<T>&) [with T = Bar]'
/opt/compiler-explorer/libs/boost_1_84_0/boost/optional/optional.hpp:1099:15: required from 'void foo() [with T = boost::optional<Bar>]'
<source>:34:34: required from here
/opt/compiler-explorer/libs/boost_1_84_0/boost/optional/optional.hpp:410:8: error: use of deleted function 'Bar::Bar(const Bar&)'
410 | ::new (m_storage.address()) unqualified_value_type(val) ;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:19:5: note: declared here
19 | Bar(Bar const&) = delete;
| ^~~
Compiler returned: 1
https://godbolt.org/z/dd3r63KG8
Почему код не работает с boost::optional?
Я пишу общую библиотеку, основанную на блоке constexpr-if с std::is_assignable_v<T>. Есть ли альтернативная характеристика типа, которую я могу использовать или написать самостоятельно, которая надежно работает для всех типов, включая boost::optional?
Редактировать: Ответ Джеффа Гаррета ссылается на шестилетнюю проблему на странице boost::optional Github. Итак, это известная проблема, и быстрым решением было бы включить специальный режим для boost::optional в мою библиотеку: https://godbolt.org/z/sdT6qdb4b
template <typename T>
struct is_copy_assignable : std::is_copy_assignable<T> {};
template <typename T>
struct is_copy_assignable<boost::optional<T>>
{
constexpr static bool value = std::is_copy_assignable_v<T> && std::is_copy_constructible_v<T>;
};
template <typename T>
constexpr bool is_copy_assignable_v = is_copy_assignable<T>::value;
На всякий случай, у меня он скомпилировался на другом компиляторе.
@KennyOstrom, какой компилятор ты использовал?





Это открытый вопрос: https://github.com/boostorg/optional/issues/54
Не существует другого признака, который можно было бы использовать, если сам тип объявляет, что его можно назначать копированием.
Спасибо за находку! Вопросу 6 лет. Затем я реализую специальную обработку для boost::options в своей библиотеке...
Он должен работать так, как вы ожидаете, в соответствии с документацией (там говорится, что необязательное значение может быть назначено только в том случае, если
Tявляется CopyConstructible и CopyAssignable), поэтому вам, вероятно, следует сообщить об ошибке для повышения.