Почему clang отвергает переменную функцию имени шаблона

У меня есть следующий пример кода, сведенный к существенному, который компилируется с gcc 6.1, gcc 7.0 head и Visual Studio 2015/2017RC, но не с какой-либо версией clang.

#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 говорит мне: prog.cc:12:34: error: '_p' является частным членом 'outer:: foo'       return std:: make_tuple (c._p...);

Я не понимаю, почему clang не распознает объявление друга. Является ли это ошибкой clang или это проблема всех других компиляторов?

Когда я делаю foo классом без шаблона, clang не жалуется. Любые идеи для обхода?

Большое спасибо заранее

Ответ 1

Как "почему?" вопрос был глубоко обсужден в этой теме Я сосредоточусь только на возможном обходном пути. Вы можете попытаться объединить вашу функцию в фиктивную структуру, чтобы гарантировать, что все возможные перегрузки ее также включены в список друзей вашего foo. Обходное предложение может выглядеть следующим образом:

#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]

Ответ 2

Мне кажется, что ошибка clang++ (это правда, что _p есть private, но bar_() должна быть функцией friend для класса foo).

Обходной путь может быть добавлен getter getP() в 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; }
};

и использовать его в bar_()

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