A function call is ambiguous when an irrelevant type is defined as an alias

After reading a large True Story article : Effective packaging I tried to implement a tuple on my own as an exercise:

#include <type_traits> #include <utility> #include <functional> template< std::size_t I, typename T > struct tuple_leaf { T value; }; template< std::size_t I, typename T > T & get(tuple_leaf< I, T > & leaf) { return leaf.value; } template< typename Is, typename ...Ts > struct tuple_base; template< std::size_t ...Is, typename ...Ts > struct tuple_base< std::index_sequence< Is... >, Ts... > : tuple_leaf< Is, Ts >... { using tuple_base_t = tuple_base; template< typename ...Args, typename = std::enable_if_t< (sizeof...(Ts) == sizeof...(Args)) > > tuple_base(Args &&... args) : tuple_leaf< Is, Ts >{std::forward< Args >(args)}... { ; } }; #if 0 template< typename ...Ts > struct tuple : tuple_base< std::index_sequence_for< Ts... >, Ts... > { using tuple_base_t = typename tuple::tuple_base_t; using tuple_base_t::tuple_base_t; using tuple_base_t::operator = ; }; #else // terse template< typename ...Ts > using tuple = tuple_base< std::index_sequence_for< Ts... >, Ts... >; #endif template< typename ...Args > tuple< Args &&... > forward_as_tuple(Args &&... args) { return {std::forward< Args >(args)...}; } #include <tuple> int main() { tuple< int > t(1); auto f = forward_as_tuple(t); (void)f; return 0; } 

Real time example

After implementing forward_as_tuple I decided to change the definition of the tuple type from the template template to the alias template of my base class template, because all I need is to split the tuple class itself and its tuple_base implementation tuple_base just std::index_sequence_for for the template type template parameters. An alias package is exactly the right tool for this purpose. After that, I get an error ( #if 0 case):

error: call 'forward_as_tuple' is ambiguous

It seems strange to me because the alias pattern does nothing, and on the other hand, forward_as_tuple calls a type from the same namespace - I was hoping that ADL should work on the case discussed above.

How to explain the difference between #if 1 and #if 0 code versions?

+5
source share
1 answer

Adl invokes the search in the passed type and the template arguments of the passed type.

The tuple nealias has its own types as well as places for ADL lookups.

In the case of a tuple alias, there is std::index_sequence in the template argument list. This leads to the consideration of std::forward_as_tuple in addition to your forward_as_tuple . They are equally good, and ambiguity arises.

As @Piotr noted in the comments, tuple<std::string> detects this problem even in the case of an alias.

+3
source

Source: https://habr.com/ru/post/1243512/


All Articles