Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ - Iterating over a tuple & resolution of type vs constant parameters

I'm currently in the process of writing arithmetic operator overloads for tuples. The operator iterates over the tuple to perform the operation on each of its individual element. Here is the definition for operator +=:

template< typename... Ts, std::size_t I = 0 >
inline typename std::enable_if< I == sizeof... (Ts), std::tuple< Ts... >& >::type operator +=(std::tuple< Ts... >& lhs, const std::tuple< Ts... >& rhs)
{
    return lhs;
}

template< typename... Ts, std::size_t I = 0 >
inline typename std::enable_if< I != sizeof... (Ts), std::tuple< Ts... >& >::type operator +=(std::tuple< Ts... >& lhs, const std::tuple< Ts... >& rhs)
{
    std::get< I >(lhs) += std::get< I >(rhs);
    return operator +=< Ts..., I + 1 >(lhs, rhs);
}

Unfortunately, when I attempt to call the operator, GCC 4.6 cannot decide which overload it should use. For example:

std::tuple< int, int, int, int > a = std::make_tuple(1, 2, 3, 4), b = std::make_tuple(5, 6, 7, 8);
a += b;

Yields the following error:

:/Workspace/raster/main.cpp:833:7:   instantiated from here
C:/Workspace/raster/main.cpp:809:45: error: no matching function for call to 'operator+=(std::tuple<int, int, int, int>&, const std::tuple<int, int, int, int>&)'
C:/Workspace/raster/main.cpp:809:45: note: candidates are:
C:/Workspace/raster/main.cpp:800:151: note: template<class ... Ts, unsigned int I> typename std::enable_if<(I == sizeof (Ts ...)), std::tuple<_TElements ...>&>::type operator+=(std::tuple<_TElements ...>&, const std::tuple<_TElements ...>&)
C:/Workspace/raster/main.cpp:806:83: note: template<class ... Ts, unsigned int I> typename std::enable_if<(I != sizeof (Ts ...)), std::tuple<_TElements ...>&>::type operator+=(std::tuple<_TElements ...>&, const std::tuple<_TElements ...>&)

Which is strange since the std::enable_if condition should reject the inappropriate call. For now, I have the following workaround which was actually my prior implementation. The above version is in fact a simplification attempt.

template< std::size_t I, typename... Ts >
inline typename std::enable_if< I == sizeof... (Ts), std::tuple< Ts... >& >::type assignadd_impl(std::tuple< Ts... >& lhs, const std::tuple< Ts... >& rhs)
{
    return lhs;
}

template< std::size_t I, typename... Ts >
inline typename std::enable_if< I != sizeof... (Ts), std::tuple< Ts... >& >::type assignadd_impl(std::tuple< Ts... >& lhs, const std::tuple< Ts... >& rhs)
{
    std::get< I >(lhs) += std::get< I >(rhs);
    return assignadd_impl< I + 1, Ts... >(lhs, rhs);
}

template< typename... Ts >
inline std::tuple< Ts... >& operator +=(std::tuple< Ts... >& lhs, const std::tuple< Ts... >& rhs)
{
    return assignadd_impl< 0, Ts... >(lhs, rhs);
}

This compiles and works as expected. Why does the simplified version refuse to compile? Thanks.

like image 767
pmjobin Avatar asked Jan 28 '11 20:01

pmjobin


People also ask

Can you iterate over a tuple?

We can iterate over tuples using a simple for-loop. We can do common sequence operations on tuples like indexing, slicing, concatenation, multiplication, getting the min, max value and so on.

Can you iterate through a tuple C++?

A C++ tuple is a container that can store multiple values of multiple types in it. We can access the elements of the tuple using std::get(), but std::get() always takes a constant variable parameter, so we can not simply iterate through it using a loop.


1 Answers

Using explicitly specified template arguments to a function or class template requires that any template parameter packs appear at the end of the overall template parameter list. Moving Ts... to the end of the template parameter lists and changing the calls appropriately makes the code work. Section 14.8.2.1 of the current C++0x draft states that parameter packs that are not at the end of the template parameter list cannot be deduced from a function call (which makes your original code fail), but explicitly specifying all of the template arguments to operator+= in all cases still causes a SFINAE error. A previous question has a link to the exact text forbidding it; IBM's documentation says that it is an error as well.

like image 148
Jeremiah Willcock Avatar answered Oct 16 '22 10:10

Jeremiah Willcock