【问题标题】:Why clang rejects variadic template friend function为什么clang拒绝可变参数模板朋友功能
【发布时间】:2016-11-21 16:27:23
【问题描述】:

我有以下示例代码,简化为基本代码,可以使用 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:错误:'_p' 是 'outer::foo' 的私有成员 返回 std::make_tuple(c._p...);

我不明白为什么 clang 不识别朋友声明。这是clang的错误还是所有其他编译器的问题?

当我将 foo 设为非模板类​​时,clang 不会抱怨。 任何解决方法的想法?

在此先感谢

【问题讨论】:

  • this 不回答你的问题吗?
  • 作为一种解决方法,您可以使用friend auto test::bar_(A&amp;&amp;, B&amp;&amp;, C&amp;&amp;... c) -&gt; decltype(std::make_tuple(c._p...)); 作为朋友(以及bar_)函数签名。 live demo
  • 我搜索了variadic和friend的组合。但我没有意识到汽车是问题所在。是的,是问题所在,明确指定返回类型解决了问题。由于实际的返回类型要复杂得多,所以我没有尝试过。非常感谢!
  • 所以也许将函数包装到一个友好的虚拟结构中会更适合你:example
  • @W.F.我进一步工作,即使使用明确的返回类型,仍然存在问题。使用 Clang 的编译现在在开始时通过,但是当它稍后进行实例化时,它再次失败。您的带有包装器的解决方案现在可以解决这个问题。如果您会“回答”,我会将其投票为我的问题的解决方案。非常感谢!

标签: c++ templates c++14 variadic-templates clang++


【解决方案1】:

作为“为什么?”这个问题在this thread 中进行了深入讨论,我将只关注一种可能的解决方法。您可以尝试将您的函数包装到一个虚拟结构中,以确保它的所有可能重载也包含在您的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 类的 friend 函数)。

    一种解决方法是在foo 中添加一个getter getP()

    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()...);
    }
    

    【讨论】:

    • 感谢您的回复。但 _p 只是我不想公开的一个简单例子。正如我上面所说,解决方法是显式声明返回类型。
    猜你喜欢
    • 1970-01-01
    • 2016-10-21
    • 1970-01-01
    • 1970-01-01
    • 2017-08-08
    • 1970-01-01
    • 1970-01-01
    • 2011-04-17
    • 2010-10-16
    相关资源
    最近更新 更多