跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • C++友元函数问题
未分類
2 9 月 2020

C++友元函数问题

C++友元函数问题

資深大佬 : Tony042 14

先上代码,注意 get 函数和其友元函数

#include <utility> #include <type_traits>  template <unsigned Height, typename T, bool = std::is_class_v<T> && !std::is_final_v<T>> class TupleElt;  template <unsigned Height, typename T> class TupleElt<Height, T, false> { private:     T value;  public:     TupleElt() = default;     template <typename U>     TupleElt(U &&other) : value(std::forward<U>(other)) {}     T &get() { return value; }     T const &get() const { return value; } };  template <unsigned Height, typename T> class TupleElt<Height, T, true> : private T { public:     TupleElt() = default;     template <typename U>     TupleElt(U &&other) : T(std::forward<U>(other)) {}     T &get() { return *this; }     T const &get() const { return *this; } };  template <unsigned H, typename T> T &getHeight(TupleElt<H, T> &te) {     return te.get(); }  template <typename... Types> class Tuple;  template <unsigned I, typename... Elements> auto get(Tuple<Elements...> &t) -> decltype(getHeight<sizeof...(Elements) - I - 1>(t)) {     return getHeight<sizeof...(Elements) - I - 1>(t); }   template <typename... Types> class Tuple;  template <typename Head, typename... Tail> class Tuple<Head, Tail...> : private TupleElt<sizeof...(Tail), Head>, private Tuple<Tail...> {     template <unsigned I, typename... Elements>     friend auto get(Tuple<Elements...> &t) -> decltype(getHeight<sizeof...(Elements) - I - 1>(t));  \ 友元函数声明  private:     using HeadElt = TupleElt<sizeof...(Tail), Head>;  public:     Head &getHead() { return static_cast<HeadElt *>(this)->get(); }     Head const &getHead() const { return static_cast<HeadElt const *>(this)->get(); }     Tuple<Tail...> &getTail() { return *this; }     Tuple<Tail...> const &getTail() const { return *this; } };  template <> class Tuple<> { };  int main() {     Tuple<char, char> t1;     get<0>(t1);     return 0 } 

我跟着 C++ templates 2nd edition, 实现了一个简单的 tuple 类和 get 函数,其中 tuple 类里面的元素被 tuple 类私有继承,get 函数通过计算元素高度,来实现直接从派生类到基类的转换来访问对应的元素,由于 tupleElt 类被私有继承,get 函数必须在 tuple 类中被声明为友元函数才可以被访问(上述代码注释处)但是即使被声明为了友元函数,gcc 还是提示 get 函数无法访问 tuple 的基类,导致编译错误,但 MSVC 可以顺利编译通过,是为什么呢,如果有错误该怎么改?

大佬有話說 (10)

  • 主 資深大佬 : Tony042

    刚才又试了下,clang-10, MSVC 16.7.0 都可以编译通过,gcc-9, gcc-10 都编译不通过…

  • 主 資深大佬 : Tony042

    果然 C++的回答就是少啊,自顶一下,另外题中代码可以通过这个链接调试 https://godbolt.org/z/Wr4arz

  • 資深大佬 : codehz

    虽然不知道发生了什么,但是你改成这样就能通过编译 https://godbolt.org/z/bq8sEn
    另外你这个方法做 tuple 居然还需要 static_cast,这肯定和正确的做法有所偏离。。。

  • 主 資深大佬 : Tony042

    @codehz 同不知道发生了什么,友元函数不行,友元类却可以,gcc 这行为挺奇怪的。stl 里面的 get 函数就是通过 static_cast 获得 tuple 里元素的值。《 C++ templates 》这本书代码有点坑,好多地方都有小错误,或者不太稳定

  • 主 資深大佬 : Tony042

    @codehz anyway 谢谢老哥的回复啊,基本上我的每个问题都是你帮我回答的,谢谢啦

  • 資深大佬 : Wirbelwind

    大概是编译器实现问题?

    使用 private 类型 继承的类型,就算是被继承类型中的 public 成员也是不能访问的。

    写个简化版的 demo 的话,clang 和 msvc 也是不给过的。

    简单的解决办法就是 private TupleElt 改成 public TupleElt

  • 主 資深大佬 : Tony042

    @Wirbelwind 应该是编译器实现问题吧,我查了下 stl 源码,msvc 的方式是将 get 函数声明为友元函数,gcc 是声明友元类。 在这个 case 里面,由于每个 tuple 类的实现都声明了所有 get 函数为友元函数,其实按理说 private 不会影响 get 函数做 Derived->Base 转换的

  • 資深大佬 : constexpr

    我研究了一下,我觉得这个问题大概跟友元有关,为此我写了一段极短的代码

    struct Base { void f() {} };

    void m(Base*) {}

    template<typename T>auto v2ex(T* b) -> decltype(m(b));

    struct Derived : private Base {
    template<typename T> friend auto v2ex(T* d) -> decltype(m(d)); //problem HERE!!!
    };

    template<typename T> auto v2ex(T* d) -> decltype(m(d)) { d->f(); }

    int main() {
    Derived d;
    v2ex(&d);
    }

    这段代码能在 MSVC, clang 中编译通过并运行, g++无法编译.

    这段代码的问题在于, 模板函数 V2EX 是类 Derived 的友元, 故可以在 V2EX 中自由转换成类 Base. 但是 V2EX 的声明有这么一段 “decltype(m(b))” , 问题是函数 m 是否应该是类 Derived 的友元呢? 如果是的话, 我传给他一个类 Dervied 的指针,他应该在 V2EX 中能畅通无阻的转换成类 Base, clang 和 MSVC 都认为是的,所以编译通过, 而 g++认为 m 不是 Derived 的友元, m 中不允许 Derived->Base 的转换, 这就是他无法编译的原因.
    对应你的代码就是 g++不认为 getHeight 是 Tuple 的友元, 所以在友元声明中 “getHeight<sizeof…(Elements) – I – 1>(t)”, g++不允许 Tuple 类型的参数 t 向基类 TupleElt 的转换!!!

    有几点说明一下:
    ■ 代码也许可以写的更短, 但是为了恰好在 MSVC 和 clang 下编译成功, 而 gcc 不成功才写的稍复杂了一点.
    ■ 你的代码比我这段代码复杂的多, 而且涉及递归继承, 多重继承. 虽然我不敢肯定我提到的这个问题就一定是你问题的解答, 但从 g++编译器给出的错误提示中, 大致就是我提出的这个问题.
    ■ 至于 ISO 标准是怎么样的, 我不清楚. 不过也能看出不同编译器对标准的解读,实现都有偏差.

  • 資深大佬 : constexpr

    @constexpr V2EX 好像自动变大写了!~

  • 資深大佬 : Wirbelwind

    @constexpr 这样理解确实不错,之前我试了在 base class 加友元函数,但是出现 ambigous 问题 就作罢了

    但是编译器报的错误是 inaccessible base of TupleElt,也可能是 gcc 不允许访问 private

    友元一定程度上破坏了 oop 特性

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具