Search code examples
c++c++20argument-dependent-lookup

Alternative to template ADL for GCC <= 10


Consider the following valid C++20 code:

#include <utility>

namespace foo
{
  template<typename... Args>
  struct tuple : Args... { };

  template<std::size_t N, typename... Args>
  auto get(tuple<Args...>) { return 0; }
}

namespace bar
{
  template<typename... Args>
  struct tuple : Args... { };

  template<std::size_t N, typename... Args>
  auto get(tuple<Args...>) { return 0; }
}

template<class Tuple, std::size_t... N>
auto for_each(Tuple& args, std::index_sequence<N...>) {
  (get<N>(args), ...);
}

int main()
{
  struct test { };
  foo::tuple<test> t;
  for_each(t, std::make_index_sequence<1>());
}

Here, get<N> is able to be resolved through ADL thanks to a C++20 addition (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0846r0.html)

My question is, what are the workarounds to this before C++20, which do not require the method for_each to know about the foo and bar namespaces ?

I'd be happy with anything that works starting from GCC 8.


Solution

  • You can add a dummy function and leave it without definition to make the compiler happy.

    When the template is instatiated the right methods will be found through ADL.

    #include <utility>
    #include <tuple>
    
    namespace foo
    {
      template<typename... Args>
      struct tuple : Args... { };
    
      template<std::size_t N, typename... Args>
      auto get(tuple<Args...>) { return 0; }
    }
    
    namespace bar
    {
      template<typename... Args>
      struct tuple : Args... { };
    
      template<std::size_t N, typename... Args>
      auto get(tuple<Args...>) { return 0; }
    }
    
    template <typename... T>
    struct Dummy;
    
    template <std::size_t N, typename... Args>
    auto get(Dummy<Args...>);
    
    template<class Tuple, std::size_t... N>
    auto for_each(Tuple& args, std::index_sequence<N...>) {
      (get<N>(args), ...);
    }
    
    int main()
    {
      struct test { };
      foo::tuple<test> t;
      for_each(t, std::make_index_sequence<1>());
    }
    

    Edit As suggested by @Jarod42 we can do

    template <std::size_t N>
    void get() = delete;
    

    The compiler can't rule out that there is a valid specialization for that template later on, so the fact that it's deleted doesn't really matter here.