请查看此代码段。我知道这没有多大意义,仅用于说明我遇到的问题:

#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;
}

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



但仅适用于Clang++。 GCC和MSVC很好用(您可以通过将代码粘贴到http://rextester.com/l/cpp_online_compiler_clang中来快速对其进行测试)

好像tBar::PrintDataAndAddress<tFoo>(const tFoo&)使用的是与tFoo相同的访问权限,在此位置将其结为好友。我知道这是因为在tFoo中加入tBar可以解决此问题。如果tBar::PrintDataAndAddress是非模板函数,该问题也将消失。

我在标准中找不到任何可以解释此行为的信息。我认为这可能是对14.6.5-temp.inject的错误解释,但我不能断言我已阅读全部内容。

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

似乎要发生此问题,被访问的私有(private)成员必须是模板函数。例如,在上面的示例中,如果
我们将PrintAddress设为非模板函数,代码将编译无误。

最佳答案

强制编译器在使用它之前实例化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;
}

这看起来像是一个编译器问题,因为它看起来与此非常相似:

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()),这两个步骤的顺序正确。

10-04 14:15