Возможность использовать указатель для функции вызова частного метода внешнего класса

Основываясь на следующем ответе на недавний вопрос, я могу использовать указатель на функцию, чтобы вызвать частный метод Foo<T>::foo() из другого класса Bar, как показано ниже (см. Также ideone)

#include <iostream>

template<typename T>
struct Bar
{
    typedef void (T::*F)();

    Bar( T& t_ , F f ) : t( t_ ) , func( f )
    {
    }

    void operator()()
    {
        (t.*func)();
    }

    F func;
    T& t;
};

template<typename T>
class Foo
{
private:
    void foo()
    {
        std::cout << "Foo<T>::foo()" << std::endl;
    }

public:    
    Foo() : bar( *this , &Foo::foo ) 
    {
        bar();
    }

    Bar<Foo<T> > bar;
};

int main()
{
    Foo<int> foo;
}

Это работает на MSVC 2013 и GCC 4.8.3. Это действительно?

Ответы

Ответ 1

Да, это допустимо, и это работает.

C++ Программирование Bjarne Stroustoup

C++ защищает от атак, а не за умышленное обход (мошенничество)

Конечно, вы не можете напрямую/легко вызывать частные методы вне класса, но если вы приложите достаточные усилия, C++ позволит это.

Ответ 2

C++ стандарт говорит

11.1. Член класса может быть
(1.1) - частный; то есть его имя может использоваться только членами и друзьями класса, в котором он объявлен.

т.е. спецификатор доступа применяется к имени, а не к исполняемому коду. Это имеет смысл, если вы думаете об этом, поскольку спецификаторы доступа являются конструкцией времени компиляции.

Ответ 3

Да, это действительно так.

Bar.operator()() просто использует указатель, не пытаясь использовать идентификатор с спецификатором частного доступа.
Не имеет значения, как был инициализирован этот указатель, если он указывает на правильную функцию.

В качестве примера рассмотрим следующее:

#include <iostream>
struct A {
protected:
    void hidden() { std::cout << "But I was hidden !?\n"; }
};
struct B : A {
    using A::hidden; // Making it public
};
int main() {
    B().hidden();
}

В стороне, не используйте std::endl если вы действительно не хотите очищать поток, как это дорого.
Обычно '\n' достаточно.

Ответ 4

Это имеет значение.

Файл заголовка

class A;
typedef int (A::*handler)(int x);
struct handler_pair {
    int code,
    handler fn
}

class A {
...
private:
  int onGoober(int x);
  int onGomer(int x);
};

Исходный файл

handler_pair handler_map[] = {
    {0, &A::onGoober},          // these will complain about the method being private
    {1, &A::onGomer}
};

Изменение handler_map на статический член в классе и инициализация таким образом позволяет избежать жалобы.

Если вы берете адрес функции-члена, важно.