Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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://rextester.com/l/cpp_online_compiler_clang)

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.

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?

It seems that for this problem to happen, the private member being accessed needs to be a template function. e.g., in the example above, if we make PrintAddress a non-template function, the code will compile without errors.

like image 466
TheProgammerd Avatar asked Oct 31 '16 20:10

TheProgammerd


People also ask

Can friend function access private data of the class?

friend Function in C++A friend function can access the private and protected data of a class.

Can we declare a template function as the friend of the class?

Template friendsBoth function template and class template declarations may appear with the friend specifier in any non-local class or class template (although only function templates may be defined within the class or class template that is granting friendship).

Can friend classes access private members C++?

Friend Class A friend class can access private and protected members of other class in which it is declared as friend. It is sometimes useful to allow a particular class to access private members of other class. For example, a LinkedList class may be allowed to access private members of Node.


1 Answers

Forcing the compiler to instantiate tBar::PrintDataAndAddress<tFoo> before using it solves the problem.

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

    auto x = &tBar::PrintDataAndAddress<tFoo>; // <= make it work....

    bar.PrintDataAndAddress(tFoo()); // Now fine

   return 0;
}

It seems to be a compiler promlem as it looks quite similar to this:

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

To be a little bit more precise... In the line bar.PrintDataAndAddress(tFoo()); the compiler has to instanciate the memberfunction tBar::PrintDataAndAddress<tFoo> and at the same time it has to resolve the friend declaration. That are internaly two seperate steps. Apparent the compiler doesn't do it in the rigth order when written in one expression. To force the compiler to instantiate bar.PrintDataAndAddress(tFoo()) first by access to the function pointer these two steps are in the right order.

like image 88
Tunichtgut Avatar answered Sep 21 '22 08:09

Tunichtgut