2017-01-29 6 views
5

Учитывая следующий код:объявляя набор функций-членов, как друг, используя шаблон

class A; 

struct B { 
    static void doIt(A* pa); 
}; 

struct C { 
    static void doIt(A* pa); 
}; 

class A { 
    int i = 9; 
    // below works but requires a line per each type 
    friend void B::doIt(A* pa); 
    friend void C::doIt(A* pa); 

    // the below however doesn't work 
    // template<typename T> 
    // friend void T::doIt(A* pa); 
     // (gcc error: member 'void T::doIt(A*)' declared as friend before type 'T' defined) 
     // (clang just ignores the above and the error is on accessing A::i in B and C)  
}; 

void B::doIt(A* pa) { 
    cout << pa->i << endl; 
} 

void C::doIt(A* pa) { 
    cout << pa->i *2 << endl; 
} 

int main() { 
    A a; 
    B::doIt(&a); 
    C::doIt(&a); 
} 

Можно ли заменить многократные friend декларации, чтобы все void T::doIt(A* pa) методов доступа к закрытым членам A?

Попытка создать экземпляр B и C выше A не помогает.

+0

'шаблон друга аннулируются T :: Doit (A * годовых);' не является полезной декларацией. Компилятор должен знать имя * class *, * function *, * class-template * или * function-template *, которое подружится с 'A'. 'T' - это не имя, которое он знает, и он не может перейти к тому, чтобы каждый член' A' был доступен для 'T' – WhiZTiM

+2

Дружба никогда не была хорошим выбором дизайна. Это всего лишь обходной путь в некоторых «отчаянных» ситуациях ... Возьмите это как возможность пересмотреть свой дизайн. –

+1

@ A.S.H: Неправда. Дружба - отличный способ улучшить инкапсуляцию. –

ответ

0

Я не вижу прямой путь, но одна работа вокруг может быть объявляя один класс с несколькими статическими методами (вместо нескольких классов с одним статическим методом), а затем объявляющего этого класса, как друга, как:

... 
struct D { 
    static void doItB(A* pa); 
    static void doItC(A* pa); 
}; 

class A { 
... 
    friend struct D; 
... 
}; 

void D::doItB(A* pa) { 
    cout << pa->i << endl; 
} 

... 
    D::doItB(&a); 
    D::doItC(&a); 
... 
+0

Идея состоит в том, что 'doIt' будет реализовываться в разных классах. Все еще ищут трюк ... –

2

Не совсем то, что вы просили, но ... если вы плачете шаблоны B, C и т. Д., Вы можете получить что-то подобное.

#include <iostream> 

class A; 

template <std::size_t> 
struct X 
{ static void doIt(A* pa); }; 

class A 
{ 
    int i = 9; 

    template <std::size_t I> 
    friend void X<I>::doIt (A* pa); 
}; 

template <> 
void X<0U>::doIt(A* pa) 
{ std::cout << pa->i << std::endl; } 

template <> 
void X<1U>::doIt(A* pa) 
{ std::cout << pa->i * 2 << std::endl; } 

template <> 
void X<2U>::doIt(A* pa) 
{ std::cout << pa->i * 3 << std::endl; } 

using B = X<0U>; 
using C = X<1U>; 
using D = X<2U>; 

int main() { 
    A a; 
    B::doIt(&a); 
    C::doIt(&a); 
    D::doIt(&a); 
} 
+0

Решение хорошо работает на g ++ (6.3.0), но генерирует предупреждение о clang (3.8.0-263969): 'предупреждение: зависимое вложенное имя спецификатора 'X ::' для объявления класса друга не поддерживается; отключить контроль доступа для «A» [-Wunsupported-friend] friend void X :: doIt (A * pa); ' Результат в clang - это то, что' A :: i' можно найти где угодно. Кажется, что clang еще не реализовал эту функцию ... –

1

Я считаю, что что-то похожее на то, что вы желаете, может быть выполнено с использованием CRTP и частного/защищенного виртуального наследования. Нижеприведенный код является только демонстрацией и определенно нуждается в некоторой работе, например. чтобы не привлекать метод шаблона дружбы:

#include <iostream> 

using namespace std; 

class base { 
protected: 
    int i = 9; 
}; 

template <class F> 
class crtp_base: virtual base { // private inheritance! 
    template <class T> 
    friend void F::doIt(T*); 
}; 

template <class... AllF> 
struct crtp_bases: crtp_base<AllF>... { }; 

struct B { 
    template <class T> 
    static void doIt(T* pa); 
}; 

struct C { 
    template <class T> 
    static void doIt(T* pa); 
}; 

class A: public crtp_bases<B, C> { 
}; 

template <class T> 
void B::doIt(T* pa) { 
    cout << pa->i << endl; 
} 

template <class T> 
void C::doIt(T* pa) { 
    cout << pa->i * 2 << endl; 
} 

int main() { 
    A a; 
    B::doIt(&a); 
    //cout << a.i << endl; // error i is private member of 'base' 
} 

[live demo]

+0

Хорошее решение. Но я не думаю, что хотел бы добавить требуемое наследование в код. –