首页 文章

在B类中声明为朋友的A类成员模板函数无法访问A类的私有成员(仅限Clang)

提问于
浏览
16

请查看此代码段 . 我知道它没有多大意义,它只是为了说明我遇到的问题:

#include <iostream>
using namespace std;

struct tBar
{
    template <typename T>
    void PrintDataAndAddress(const T& thing)
    {
        cout << thing.mData;
        PrintAddress<T>(thing);
    }

private:
    // friend struct tFoo; // fixes the compilation error

    template <typename T>
    void PrintAddress(const T& thing)
    {
        cout << " - " << &thing << endl;
    }
};

struct tFoo
{
    friend void tBar::PrintDataAndAddress<tFoo>(const tFoo&);
    private:

    int mData = 42;
};

struct tWidget
{
    int mData = 666;
};

int main() 
{
    tBar bar;
    bar.PrintDataAndAddress(tWidget()); // Fine

    bar.PrintDataAndAddress(tFoo()); // Compilation error

    return 0;
}

上面的代码触发以下错误:

source_file.cpp:10:3:错误:'PrintAddress'是'tBar'PrintAddress(thing)的私有成员; source_file.cpp:42:6:注意:在实例化函数模板>特殊化'tBar :: PrintDataAndAddress'这里请求bar.PrintDataAndAddress(tFoo()); //编译错误source_file.cpp:17:7:注意:声明私有这里void PrintAddress(const T&thing)

但只有在Clang . GCC和MSVC都可以(你可以通过在http://rextester.com/l/cpp_online_compiler_clang中粘贴代码来快速测试)

似乎 tBar::PrintDataAndAddress<tFoo>(const tFoo&) 使用与 tFoo 相同的访问权限,在那里它是友好的 . 我知道这是因为在 tBar 中与 tFoo 交朋友修复了这个问题 . 如果 tBar::PrintDataAndAddress 是非模板函数,问题也会消失 .

我无法在标准中找到解释此行为的任何内容 . 我相信这可能是对14.6.5 - temp.inject的错误解释,但我无法声称我已经阅读了所有内容 .

有没有人知道Clang是否正确无法编译上述代码?如果是这样的话,你能否引用相关的C标准文本?

似乎要发生这个问题,被访问的私有成员需要是模板功能 . 例如,在上面的例子中,如果我们使PrintAddress成为非模板函数,代码将编译而没有错误 .

2 回答

  • 5

    在使用之前强制编译器实例化 tBar::PrintDataAndAddress<tFoo> 解决了问题 .

    int main() 
    {
        tBar bar;
        bar.PrintDataAndAddress(tWidget()); // Fine
    
        auto x = &tBar::PrintDataAndAddress<tFoo>; // <= make it work....
    
        bar.PrintDataAndAddress(tFoo()); // Now fine
    
       return 0;
    }
    

    它似乎是一个编译器promlem,因为它看起来非常相似:

    In C++, why isn't it possible to friend a template class member function using the template type of another class?

    为了更精确一点......在 bar.PrintDataAndAddress(tFoo()); 行中,编译器必须实现成员函数 tBar::PrintDataAndAddress<tFoo> ,同时它必须解析好友声明 . 这是两个单独的步骤 . 显然,当编写一个表达式时,编译器不会按照严格顺序执行此操作 . 要强制编译器首先通过访问函数指针来实例化 bar.PrintDataAndAddress(tFoo()) ,这两个步骤的顺序正确 .

  • -3

    尝试在友元功能之前添加此功能

    template <typename tFoo>
    

相关问题