Std:: get использовать enum class в качестве аргумента шаблона
Я использую std::tuple
и определяю перечисление класса, чтобы каким-то образом "называть" каждый из полей кортежа, забывая об их фактических индексах.
Итак, вместо этого:
std::tuple<A,B> tup;
/* ... */
std::get<0>(tup) = bleh; // was it 0, or 1?
Я сделал это:
enum class Something {
MY_INDEX_NAME = 0,
OTHER_INDEX_NAME
};
std::tuple<A,B> tup;
/* ... */
std::get<Something::MY_INDEX_NAME> = 0; // I don't mind the actual index...
Проблема в том, что, поскольку это скомпилировано с использованием gcc 4.5.2, я теперь установил версию 4.6.1, и мой проект не удалось скомпилировать. Этот фрагмент воспроизводит ошибку:
#include <tuple>
#include <iostream>
enum class Bad {
BAD = 0
};
enum Good {
GOOD = 0
};
int main() {
std::tuple<int, int> tup(1, 10);
std::cout << std::get<0>(tup) << std::endl;
std::cout << std::get<GOOD>(tup) << std::endl; // It OK
std::cout << std::get<Bad::BAD>(tup) << std::endl; // NOT!
}
Ошибка в основном говорит, что нет перегрузки, которая соответствует моему вызову std::get
:
test.cpp: In function ‘int main()’:
test.cpp:16:40: error: no matching function for call to ‘get(std::tuple<int, int>&)’
test.cpp:16:40: note: candidates are:
/usr/include/c++/4.6/utility:133:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/utility:138:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> const typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(const std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/tuple:531:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(std::tuple<_Elements ...>&)
/usr/include/c++/4.6/tuple:538:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_c_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(const std::tuple<_Elements ...>&)
Итак, есть ли способ использовать мой класс перечисления в качестве аргумента шаблона для std::get
? Было ли это что-то, что не собиралось компилировать, и было исправлено в gcc 4.6? Я мог бы использовать простую переимену, но мне нравятся свойства выделения классов перечисления, поэтому я бы предпочел использовать последнее, если это возможно.
Ответы
Ответ 1
Сильно типизированные перечисления, введенные С++ 11, не могут быть неявно преобразованы в интегральные значения типа say int
, а std::get
ожидает, что аргумент шаблона будет интегральным типом.
Вы можете использовать static_cast
для преобразования значений перечисления:
std::cout <<std::get<static_cast<int>(Bad::BAD)>(tup)<< std::endl; //Ok now!
Или вы можете выбрать преобразование в базовый интегральный тип как:
//note that it is constexpr function
template <typename T>
constexpr typename std::underlying_type<T>::type integral(T value)
{
return static_cast<typename std::underlying_type<T>::type>(value);
}
то используйте его как:
std::cout <<std::get<integral(Bad::BAD)>(tup)<< std::endl; //Ok now!
Ответ 2
Я хотел бы добавить еще один ответ, потому что исходный плакат попросил способ иметь именованный доступ к элементам std:: tuple через перечисление класса.
Возможно иметь шаблонный аргумент типа перечисления класса (по крайней мере, в GCC). Это позволяет определить собственный get
для получения элемента кортежа с учетом значения перечисления класса. Ниже приведена реализация, использующая это значение для int, но вы также можете сделать что-то более фантастическое:
#include <tuple>
enum class names { A = 0, B, C };
template< names n, class... Types >
typename std::tuple_element<static_cast< std::size_t >( n ), std::tuple<Types...> >::type&
get( std::tuple<Types...>& t )
{
return std::get< static_cast< std::size_t >( n ), Types... >( t );
}
int main( int, char** )
{
std::tuple< char, char, char > t( 'a', 'b', 'c' );
char c = get<names::A>( t );
}
Обратите внимание, что std::get
имеет еще два варианта (один для const tuple&
, один для tuple&&
), который может быть точно реализован Таким же образом.
Ответ 3
Да, это была ошибка в GCC 4.5. Области ограниченных перечислений не имеют неявных преобразований в интегральные типы.
Ответ 4
Совершенно другое решение:
A& my_field(std::tuple<A,B>& t) { return std::get<0>(t); }
A const& my_field(std::tuple<A,B> const& t) { return std::get<0>(t); }
B& my_other_field(std::tuple<A,B>& t) { return std::get<1>(t); }
B const& my_other_field(std::tuple<A,B> const& t) { return std::get<1>(t); }
my_field(t) = blah;
my_other_field(t) = frob;
Ответ 5
Мое решение заключается в использовании:
namespace Something{enum class Something {MY_INDEX_NAME = 0,OTHER_INDEX_NAME};};