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