В моем проекте я использую Boost.Bimap для реализации двунаправленных карт.
Посмотрите на этот очень простой MCVE на godbolt , где я использую структурированное связывание , чтобы напечатать пару ключ-значение правильной карты (которая, согласно документации, является совместимый с подписью std::map
.
Задача
Прекрасно компилируется для любой версии g ++> = 7.4 и выше, однако мне нужно использовать g ++ 7.1. и здесь этот код завершается с помощью следующего сообщения:
<source>: In function 'int main()':
<source>:11:20: error: 'std::tuple_size<const boost::bimaps::relation::structured_pair<boost::bimaps::tags::tagged<const long unsigned int, boost::bimaps::relation::member_at::right>, boost::bimaps::tags::tagged<const std::__cxx11::basic_string<char>, boost::bimaps::relation::member_at::left>, mpl_::na, boost::bimaps::relation::mirror_layout>>::value' is not an integral constant expression
for (const auto& [key, value] : bm.right) {
Мне удалось выяснить, что это связано с ошибкой в g ++, которая, похоже, была исправлена в более поздних версиях.
Попытка обхода (например, игрушка, успешная)
Чтобы структурированные привязки работали с моей версией компилятора, я попытался создать обходной путь, специализируя std::tuple_size
, std::tuple_element
и std::get
. См. эту ссылку cppreference для получения дополнительной информации.
Для простоты я сначала успешно попробовал это с игрушечной структурой. Вот специализации, посмотрите полный код на godbolt.org :
struct SampleType {
int a = 42;
std::string b = "foo"s;
double c = 3.141;
};
#if (__GNUC__ == 7) && (__GNUC_MINOR__ == 1)
template <std::size_t N>
decltype(auto) get(const ::SampleType& t) {
if constexpr (N==0) return t.a;
else if constexpr (N==1) return t.b;
else return t.c;
}
namespace std {
// Tuple size is 3
template <> struct tuple_size<::SampleType> : std::integral_constant<std::size_t, 3> {};
// Define tuple types
template <std::size_t N> struct tuple_element<N, ::SampleType> {
// Deduce type from get() function template defined above
using type = decltype(::get<N>(std::declval<::SampleType>()));
};
}
#endif
Обратите внимание, что если вы удалите #ifdef
для g ++ 7.1., Компиляция завершится с той же ошибкой, что и выше (...is not an integral constant expression
). (Интересно: в отличие от примера boost::bimap
, который прекрасно компилируется только с g ++ 7.4 и далее, пример с игрушкой уже успешно работает с g ++ 7.2)
Попытка обхода (оригинальный пример, не удачный)
Теперь, будучи очень убежденным, что я нашел решение, я попытался сделать то же самое для boost::bimap
, но я беспомощно провалился ( проверьте это на godbolt.org ):
template <std::size_t N>
decltype(auto) get(const bimap::right_map::value_type& bm) {
if constexpr (N==0) return bm.first;
else if constexpr (N==1) return bm.second;
}
namespace std {
// Tuple size is 2 -> key-value pair
template <> struct tuple_size<bimap::right_map::value_type> : std::integral_constant<std::size_t, 2> {};
// Define tuple types
template <> struct tuple_element<0, bimap::right_map::value_type> { using type = std::string; };
template <> struct tuple_element<1, bimap::right_map::value_type> { using type = std::size_t; };
}
Сообщение об ошибке слишком длинное, чтобы публиковать здесь (см. Вывод godbolt), но в основном я понимаю, что компилятор не сопоставляет перегрузку для "my" get
. Обратите внимание, что по причинам отладки я вставил следующую строку в мой код, чтобы убедиться, что я действительно имею дело с правильным типом в моих специализациях.
for (const auto& pair : bm.right) {
// Make sure we capture the right type in the specializations above
static_assert(std::is_same<
decltype(pair),
const bimap::right_map::value_type&
>::value);
}
Я что-то не так делаю? Или эта ошибка представляет собой непреодолимое препятствие моей попытке обхода?