Felix Petriconi Felix Petriconi - 16 days ago 6
C++ Question

Why clang rejects variadic template friend function

I have the following sample code, reduced to the essential, that compiles with gcc 6.1, gcc 7.0 head and Visual Studio 2015/2017RC, but not with any clang version.

#include <iostream>
#include <tuple>

using namespace std;

namespace outer {
namespace test {

template <typename A, typename B, typename...C>
auto bar_(A&&, B&&, C&&... c) {
return std::make_tuple(c._p...);
}

}

template <typename A, typename B, typename...C>
auto bar(A a, B b, C&&... c) {
return test::bar_(std::move(a), std::move(b), std::forward<C>(c)...);
}

template<typename T>
class foo
{
template <typename A, typename B, typename...C>
friend auto test::bar_(A&&, B&&, C&&... c);

int _p;
public:
foo(int f) : _p(f) {}
};
}

int main() {
outer::foo<int> a1(1);
outer::foo<int> a2(2);

auto result = outer::bar(2.3, 4.5, a1, a2);
cout << get<0>(result) << " " << get<1>(result) << '\n';

return 0;
}


clang tells me:
prog.cc:12:34: error: '_p' is a private member of 'outer::foo'
return std::make_tuple(c._p...);

I don't understand why clang does not recognize the friend declaration. Is this a bug of clang or is it a problem of all the other compilers?

When I make foo a non template class, clang does not complain.
Any ideas for a workaround?

Many thanks in advance

Answer

As the "why?" question was deeply discussed in this thread I will focus only on a possible workaround. You could try to wrap your function into a dummy struct just to ensure all the possible overloads of it are also included in a friend-list of your foo. The workaround proposition could look as follows:

#include <iostream>
#include <tuple>

using namespace std;

namespace outer {
  namespace test {

    struct wrapper{
      template <typename A, typename B, typename...C>
      static auto bar_(A&&, B&&, C&&... c) {
        return std::make_tuple(c._p...);
      }
    };

  }

  template <typename A, typename B, typename...C>
  auto bar(A a, B b, C&&... c) {
    return test::wrapper::bar_(std::move(a), std::move(b), std::forward<C>(c)...);
  }

  template<typename T>
  class foo
  {
    friend struct test::wrapper;

    int _p;
  public:
    foo(int f) : _p(f) {}
  };
}

int main() {
  outer::foo<int> a1(1);
  outer::foo<int> a2(2);

  auto result = outer::bar(2.3, 4.5, a1, a2);
  cout << get<0>(result) << " " << get<1>(result) << '\n';

  return 0;
}

[live demo]