lundi 31 octobre 2016

Class A member template function declared as friend in class B can't access private members of class A (Clang only)

Sorry for the title, it is a mouthful, but I could not find a good compromise between readable and specific.

Please take a look to this code snippet. I know it does not make much sense, it is just intended to illustrate the problem I am encountering:

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

The code above triggers the following error:

source_file.cpp:10:3: error: 'PrintAddress' is a private member of 'tBar' PrintAddress(thing); source_file.cpp:42:6: note: in instantiation of function template >specialization 'tBar::PrintDataAndAddress' requested here bar.PrintDataAndAddress(tFoo()); // Compilation error source_file.cpp:17:7: note: declared private here void PrintAddress(const T& thing)

but only in Clang++. GCC and MSVC are fine with it (you can quickly test that by pasting that code in http://ift.tt/2bynScT)

It seems as if tBar::PrintDataAndAddress<tFoo>(const tFoo&) is using the same access as tFoo, where it is befriended. I know this because befriending tFoo in tBar fixes this issue. The problem also goes away if tBar::PrintDataAndAddress is a non-template function.

For the life of me, I have not been able to find anything in the Standard that explains this behavior. I believe it could be a bad interpretation of 14.6.5 - temp.inject, but I can't claim I have read all of it.

Does anyone know if Clang is right failing to compile the above code? Can you please quote the relevant C++ standard text if that is the case?

Aucun commentaire:

Enregistrer un commentaire