简体   繁体   中英

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

It seems to me a clang++ bug (it's true that _p is private , but bar_() should be a friend function for foo class).

A workaround could be add a getter getP() in foo

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

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

  int getP () const  //  <--- added getP()
   { return _p; }
};

and use it in bar_()

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

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]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM