Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
325 views
in Technique[技术] by (71.8m points)

c++11 - For std::tuple, how to get data by type, and how to get type by index?

The title says it: Given an std::tuple, I'd like to

  1. get the first element of a given type
  2. get the type of the i-th element

Is there a solution provided by STL? Or a workaround? Can someone try to complete my code?

#include <tuple>

int main ()
{
    std::tuple<int,char,int> mytuple (10,'a', 5);

    // how to get the first int element here? (10)
    // int x = std::get_me_the_first<int>(mytuple);

    // how to get the type of the second element here?
    // std::get_me_type_of<1> ch = 'x';

    return 0;
}

Compiled like this:

g++ -std=c++11 -Wall main.cpp -o main
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Getting value from tuple by type (instead of index)

As of C++11, there is no STL way to get the first element of a tuple of type T.
In C++14, there should be a way using a new overload of std::get to do what you want. The ISO paper is located here N3404 and here N3670.

You can do this in C++11 with the following:

#include<tuple>
#include<type_traits>
#include<string>
#include<iostream>

template<int Index, class Search, class First, class... Types>
struct get_internal
{
    typedef typename get_internal<Index + 1, Search, Types...>::type type;
        static constexpr int index = Index;
};

template<int Index, class Search, class... Types>
struct get_internal<Index, Search, Search, Types...>
{
    typedef get_internal type;
    static constexpr int index = Index;
};

template<class T, class... Types>
T get(std::tuple<Types...> tuple)
{
    return std::get<get_internal<0,T,Types...>::type::index>(tuple);
}

I have it hosted on Ideone here, but here's my test function for posterity

int main()
{
    std::tuple<int, double, std::string> test{1, 1.7, "test"};
    std::cout<<"get<0> == get<int> :"<< (std::get<0>(test) == get<int>(test))<< "
";
    std::cout<<"get<1> == get<double> :"<<(std::get<1>(test) == get<double>(test))<< "
";
    std::cout<<"get<2> == get<std::string> :"<<(std::get<2>(test) == get<std::string>(test))<< "
";
}

Based of @Yakk's idea of extending this to support multiple instances of a type as well as a predicate to test for in the tuple, he provided the code below (also hosted on Ideone here)
Be warned: in C++14 the new overload of std::get does not allow multiple instances of the same type in the tuple. It instead issues a compile error. In addition the C++14 version will not support predicates either.

//Include same headers as before
template<bool b, typename T=void>
using EnableIf = typename std::enable_if<b,T>::type;

template<int Index, template<typename T>class Search, int Which, typename, class First, class... Types>
struct get_internal:
    get_internal<Index + 1, Search, Which, void, Types...>
{};

template<int Index, template<typename T>class Search, int Which, class First, class... Types>
struct get_internal<Index, Search, Which, EnableIf<!Search<First>::value>, First, Types...>:
    get_internal<Index + 1, Search, Which, void, Types...>
{};
template<int Index, template<typename T>class Search, int Which, class First, class... Types>
struct get_internal<Index, Search, Which, EnableIf<Search<First>::value>, First, Types...>:
    get_internal<Index + 1, Search, Which-1, void, Types...>
{};
template<int Index, template<typename T>class Search, class First, class... Types>
struct get_internal<Index, Search, 0, EnableIf<Search<First>::value>, First, Types...>:
    std::integral_constant<int, Index>
{};

template<template<typename>class Test, int Which=0, class... Types>
auto get(std::tuple<Types...>& tuple)->
  decltype(std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple))
{
    return std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple);
}
template<template<typename>class Test, int Which=0, class... Types>
auto get(std::tuple<Types...> const& tuple)->
  decltype(std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple))
{
    return std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple);
}
template<template<typename>class Test, int Which=0, class... Types>
auto get(std::tuple<Types...>&& tuple)->
  decltype(std::move(std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple)))
{
    return std::move(std::get<get_internal<0,Test,Which,void,Types...>::value>(tuple));
}

template<typename T>
struct is_type {
  template<typename U>
  using test = std::is_same<T,U>;
};

template<class T, int Which=0, class... Types>
T& get(std::tuple<Types...>& tuple)
{
    return get<is_type<T>::template test,Which>(tuple);
}
template<class T, int Which=0, class... Types>
T const& get(std::tuple<Types...> const& tuple)
{
    return get<is_type<T>::template test,Which>(tuple);
}
template<class T, int Which=0, class... Types>
T&& get(std::tuple<Types...>&& tuple)
{
    return std::move(get<is_type<T>::template test,Which>(tuple));
}

Getting type of n-th element in tuple

There is a way to get the type of the n-th element. std::tuple_element<n, decltype(tuple)>::type (thanks @syam) is the type of the n-th element of the tuple.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...