2

It's not secret, std::get<i>(tuple) annoys many programmers.

Instead of it, I want use something like tuple[i].

So I tried to simulate it.

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

template < int > struct index{};



template< char ... >
struct combine;

template<> struct combine<> : std::integral_constant< int , 0>{};


constexpr int ten(size_t p)noexcept
{
    return p == 0 ? 1 : 10 * ten(p-1);
}

template< char c, char ... t>
struct combine<c, t...> : std::integral_constant< int, (c - '0')*ten(sizeof...(t)) + combine<t...>::value > 
{ static_assert(c >= '0' && c <= '9', "only 0..9 digits are allowed");  };


template< char ... c >
constexpr auto  operator "" _index()noexcept 
{ 
    return index< combine<c...>::value >{}; 
}; 

template< class ... Args >
struct mytuple : public std::tuple<Args...>
{
    using std::tuple<Args...>::tuple;

    template< int i >
    auto& operator []( index<i> ) noexcept
    {
        return std::get< i > ( static_cast< std::tuple<Args...> & >(*this) );
    }

    template< int i>
    auto const& operator [](index<i> )const noexcept
    {
        return std::get< i >(static_cast< std::tuple<Args...> const& >(*this) );
    }

};


int main()
{
     static_assert( combine<'1','2','3','4'>::value == 1234, "!");


     static_assert( std::is_same< index<785>, decltype( 785_index ) > {}, "!");

     using person = mytuple< std::string, int, double, char>;

     person s = std::make_tuple("Bjarne Stroustrup", 63, 3.14, '7' );
     auto name = s[ 0_index ];
     auto old  = s[ 1_index ];
     auto number = s[ 2_index ];
     auto symbol = s[ 3_index ];

     std::cout << "name: "   << name << '\t'
               << "old: "    << old << '\t'
               << "number: "  << number<< '\t'
               << "symbol: " << symbol<< '\t'
               << std::endl;
}

Q: What's wrong this code? i.e this code is usable or not? If is usable why isn't std::tuple implemented like this?

Khurshid Normuradov
  • 1,564
  • 1
  • 13
  • 20
  • 4
    It's a lot of work for very little gain IMO. I also think there's a bug in this sample. Try `0x1_index` or even `1.0_index`. – Simple Oct 10 '13 at 10:41
  • I'd also say it's not worth it. The annoying thing with `std::get(tuple)` is that it can't be used with a variable, not that it can't be done via the index operator. Your implementation still doesn't provide accessing the tuple via a simple variable. – πάντα ῥεῖ Oct 10 '13 at 10:48
  • @g-makulik. std::get< i > (tuple) is glibly, so I don't like it. C++ - is static typed language, in C++ you must not access tuple element via a simple variable at runtime. – Khurshid Normuradov Oct 10 '13 at 10:53
  • 1
    For one thing, `tuple` existed long before user-defined literals. And I personally think that appending `_index` is just not worth it. – Xeo Oct 10 '13 at 11:10
  • `std::get(tuple)` doesn't annoy me. – R. Martinho Fernandes Oct 10 '13 at 11:27
  • I'm tempted to mark this as duplicate of [How do I access a Tuple like an Array (with bracket overload and type safety)?](http://stackoverflow.com/questions/15950924/how-do-i-access-a-tuple-like-an-array-with-bracket-overload-and-type-safety#comment22730134_15950924) – Marco A. Sep 14 '15 at 12:43

1 Answers1

5

I'm not really sure what the specific question in your case is, but it seems you are looking for a little bit of convenience. The following uses the placeholders (starting with _1) to simplify your code:

#include <iostream>
#include <type_traits>
#include <tuple>
#include <functional>

template< class ... Args >
struct mytuple : public std::tuple<Args...>
{
    using std::tuple<Args...>::tuple;

    template< typename T >
    auto& operator []( T ) noexcept
    {
        return std::get< std::is_placeholder<T>::value - 1 >( *this );
    }

    template< typename T >
    auto const& operator []( T ) const noexcept
    {
        return std::get< std::is_placeholder<T>::value - 1 >( *this );
    }
};

int main()
{
    using person = mytuple< std::string, int, double, char>;

    using namespace std::placeholders;

    person s = std::make_tuple("Bjarne Stroustrup", 63, 3.14, '7' );
    auto name = s[ _1 ];
    auto old  = s[ _2 ];
    auto number = s[ _3 ];
    auto symbol = s[ _4 ];

    std::cout << "name: "   << name << '\t'
              << "old: "    << old << '\t'
              << "number: "  << number<< '\t'
              << "symbol: " << symbol<< '\t'
              << std::endl;
}

The trick here is to know that std::is_placeholder<T> is guaranteed to be derived from std::integral_constant<int,N> :) Hope you like it.

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • 2
    Pity it's not zero-based. – R. Martinho Fernandes Oct 10 '13 at 11:28
  • The `static_cast` is unnecessary in this case. – Xeo Oct 10 '13 at 11:34
  • It's good, but 1) it's not zero-based. 2) maximal _N - is implementation defined by compiler. I'm not interesting how to implement this, I'm interesting why this way isn't popular or isn't used? – Khurshid Normuradov Oct 10 '13 at 11:40
  • @Xeo Indeed, fixed. Also, this uses `auto` type specifier without trailing return type, hence strictly speaking it's C++14. That's the same as OPs code and it would be easy to fix for C++11, so I'll leave it as is. – Daniel Frey Oct 10 '13 at 11:42
  • 6
    @KhurshidNormuradov As others already said, `std::get(tuple)` is not too annoying to justify the effort for the above. Also, simply consider using a small struct instead: `struct person { std::string name; int old; double number; char symbol; };`. – Daniel Frey Oct 10 '13 at 12:02