12

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

max66
  • 65,235
  • 10
  • 71
  • 111
Felix Petriconi
  • 675
  • 5
  • 11
  • 3
    Doesn't [this](http://stackoverflow.com/questions/32889492/friend-function-template-with-automatic-return-type-deduction-cannot-access-a-pr) answer your question? – W.F. Nov 21 '16 at 16:53
  • 1
    As a workaround you could use `friend auto test::bar_(A&&, B&&, C&&... c) -> decltype(std::make_tuple(c._p...));` as a friend (as well as a `bar_`) function signature. [live demo](http://melpon.org/wandbox/permlink/CEBDjgZGGLbAtWY1) – W.F. Nov 21 '16 at 17:03
  • I searched for the combination of variadic and friend. But I did not realize the auto was the problem. Yes, is is the problem and explicit specifying the return type solved the problem. Since the actual return type is much more complicated, I did not tried it. Many thanks! – Felix Petriconi Nov 22 '16 at 07:32
  • So maybe wrapping function into a friended dummy struct would suit you better: [example](http://melpon.org/wandbox/permlink/K5pNORw4zZjeguKq) – W.F. Nov 22 '16 at 09:51
  • @W.F. I worked further and even with an explicit return type, there are still problems. The compilation with Clang now passes at the beginning, but when it comes later to an instantiation it again fails. Your solution with a wrapper solves that now. If you would have "answered", I would vote it as solution for my problem. Many thanks! – Felix Petriconi Nov 23 '16 at 13:40

2 Answers2

1

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()...);
}
max66
  • 65,235
  • 10
  • 71
  • 111
  • Thanks for the response. But _p was just a simple example of something that I do not want to make public. As I stated above the workaround is to explicit declare the returns type. – Felix Petriconi Nov 22 '16 at 09:00
1

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]

Community
  • 1
  • 1
W.F.
  • 13,888
  • 2
  • 34
  • 81