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

时间:2016-10-31 20:46:43

标签: c++ c++11 templates clang++

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

#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(事);   source_file.cpp:42:6:注意:在实例化函数模板&gt;特化'tBar :: PrintDataAndAddress'这里请求          bar.PrintDataAndAddress(tFoo()); //编译错误   source_file.cpp:17:7:注意:在这里声明私有          void PrintAddress(const T&amp; thing)

但仅限于Clang ++。 GCC和MSVC很好用(你可以通过在http://rextester.com/l/cpp_online_compiler_clang中粘贴代码来快速测试)

似乎tBar::PrintDataAndAddress<tFoo>(const tFoo&)使用与tFoo相同的访问权限,而tFoo与其成为朋友。我知道这一点,因为tBar中的tBar::PrintDataAndAddress朋友正在解决此问题。如果SELECT cid.InvoiceCode, SUM(cid.ExtPrice) + MAX(ci.Freight) AS 'Sales' FROM CustomerInvoice ci JOIN CustomerInvoiceDetail cid ON ci.InvoiceCode = cid.InvoiceCode GROUP BY cid.InvoiceCode 是非模板函数,问题也会消失。

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

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

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

2 个答案:

答案 0 :(得分:6)

强制编译器在使用它之前实例化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()),这两个步骤的顺序正确。

答案 1 :(得分:-3)

尝试在友元功能

之前添加此功能
template <typename tFoo>