Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::get using enum class as template argument

I'm using a std::tuple and defined a class enum to somehow "naming" each of the tuple's fields, forgetting about their actual indexes.

So instead of doing this:

std::tuple<A,B> tup;
/* ... */
std::get<0>(tup) = bleh; // was it 0, or 1?

I did this:

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...

The problem is that, as this compiled using gcc 4.5.2, I've now installed the 4.6.1 version, and my project failed to compile. This snippet reproduces the error:

#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's OK
    std::cout << std::get<Bad::BAD>(tup) << std::endl; // NOT!
}

The error basically says that there's no overload that matches my call to 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 ...>&)

So, is there any way I can use my enum class as a template argument to std::get? Was this something that wasn't ment to compile, and was fixed in gcc 4.6? I could use a simple enum, but I like the scoping properties of enum classes, so I'd prefer to use the latter if it's possible.

like image 234
mfontanini Avatar asked May 24 '12 17:05

mfontanini


3 Answers

The strongly-typed enums introduced by C++11 cannot be implicitly converted into integral values of type say int, while std::get expects the template argument to be integral type.

You've to use static_cast to convert the enum values:

std::cout <<std::get<static_cast<int>(Bad::BAD)>(tup)<< std::endl; //Ok now!

Or you can choose to convert into underlying integral type as:

//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);
}

then use it as:

std::cout <<std::get<integral(Bad::BAD)>(tup)<< std::endl; //Ok now!
like image 73
Nawaz Avatar answered Nov 19 '22 23:11

Nawaz


I'd like to add another answer because the original poster asked for a way to have a named access to elements of std::tuple through a class enum.

It is possible to have a template argument of the type of a class enum (at least in GCC). This makes it possible to define your own get retrieving the element of a tuple given a class enum value. Below is an implementation casting this value to int, but you could also do something more fancy:

#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 );
}

Note that std::get has two more variants (one for const tuple&, one for tuple&&), which can be implemented exactly the same way.

like image 30
ingomueller.net Avatar answered Nov 19 '22 23:11

ingomueller.net


Yes, this was a bug in GCC 4.5. Scoped enums don't have implicit conversions to integral types.

like image 3
R. Martinho Fernandes Avatar answered Nov 19 '22 21:11

R. Martinho Fernandes