Функция шаблона члена класса, объявленная как friend в классе B, не может получить доступ к закрытым членам класса A (только 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: примечание: при создании шаблона функции > специализация 'tBar:: PrintDataAndAddress' запрошена здесь        bar.PrintDataAndAddress(TFoo());//Ошибка компиляции source_file.cpp: 17: 7: note: объявлено приватным здесь        void PrintAddress (const T & вещь)

но только в 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 не смог скомпилировать вышеуказанный код? Можете ли вы указать соответствующий стандартный текст на С++, если это так?

Кажется, что для этой проблемы доступ к частному члену должен быть функцией шаблона. например, в приведенном выше примере, если мы делаем PrintAddress функцией без шаблона, код будет компилироваться без ошибок.

Ответы

Ответ 1

Принуждение компилятора к экземпляру 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, поскольку он выглядит очень похоже на это:

В С++ почему не удается сопоставить функцию-член класса шаблона с использованием типа шаблона другого класса?

Чтобы быть немного точнее... В строке bar.PrintDataAndAddress(tFoo()); компилятор должен инициализировать функцию-член tBar::PrintDataAndAddress<tFoo> и в то же время он должен разрешить объявление друга. Это внутренне два отдельных шага. По-видимому, компилятор не делает этого в строгом порядке, когда он написан в одном выражении. Чтобы заставить компилятор создать экземпляр bar.PrintDataAndAddress(tFoo()) сначала путем доступа к указателю функции, эти два шага находятся в правильном порядке.

Ответ 2

Попробуйте добавить это перед функцией друга

template <typename tFoo>