2012-02-17 1 views
1

Рассмотрим этот код:Как избежать ошибки переопределения в случае определения класса функции в классе?

template<typename T> 
class Base 
{ 
    template<typename U> 
    friend void f(void *ptr) { 
    static_cast<Base<U>*>(ptr)->run(); 
    } 
    protected: 
     virtual void run() = 0; 
}; 

class A : public Base<A> 
{ 
    protected: 
     virtual void run() {} 
}; 

/* 
class B : public Base<B> 
{ 
    protected: 
     virtual void run() {} 
}; 
*/ 

Он компилирует нормально сейчас (ideone). Но если я раскомментировать определение B, то это дает следующее сообщение об ошибке (ideone):

prog.cpp: In instantiation of ‘Base<B>’: 
prog.cpp:20: instantiated from here 
prog.cpp:6: error: redefinition of ‘template<class U> void f(void*)’ 
prog.cpp:6: error: ‘template<class U> void f(void*)’ previously defined here 

Я знаю (хорошо, я думаю, я знаю) причину, почему он дает эту ошибку.

Так что мой вопрос:

Как избежать ошибок переопределения в случае в классе определения шаблона друга функции?

До тех пор, пока я даю определение основного шаблона (не специализации) внутри класса, я получу эту ошибку. Существует также другая проблема с определением первичного шаблона таким образом: он делает все экземпляры шаблона ffriend всех экземпляров шаблона класса Base, которого я также хотел бы избежать. Я хочу сделать f<T> другом Base<T>, но не f<U> другу Base<T>, если U и T не то же самое. В то же время я также хочу дать определение внутри класса. Является ли это возможным?

+0

Я не вижу причин, почему компилятор должен без ошибок на этом, и это, кажется, лязг люди не видят причины тоже, как он компилирует с clang. – PlasmaHH

+0

GCC и MSVC10 оба дают ошибку, если я раскомментирую определение 'B'. – Nawaz

+0

@PlasmaHH: поскольку функция friend не является функцией-членом и поэтому может быть не зависящей от аргументов шаблона класса. –

ответ

4

Вам действительно нужно определить f в класс? Если определить его снаружи, ваша проблема исчезает, и вы можете также применять отношения к один-к-одному вы хотите (т.е. только f<T> является другим Base<T>):

template <typename T> class Base; 

template <typename U> 
void f(void *ptr) { 
    static_cast<Base<U>*>(ptr)->run(); 
} 

template<typename T> 
class Base 
{ 
    friend void f<T>(void *ptr); //only one instanciation is a friend 

    protected: 
    virtual void run() = 0; 
}; 

Однако следует отметить, что тот факт, что только f<T> является другом Base<T> не помешает следующий код при компиляции:

B b; 
f<A>(&b); // compiles, f<A> calls Base<A>::run, but the cast is wrong 
1

Функция друга - это глобальная функция, даже если вы введете ее реализацию в тело любого класса. Проблема заключается в том, что при создании экземпляра Base<T> дважды (в любом контексте) вы предоставляете две реализации f. Обратите внимание, что f делает не зависит от T, и он не может использовать T; это то же самое для всех Base<T>.

Простое решение предоставить только декларацию f внутри шаблона класса и реализации за ее пределами:

template<typename T> 
class Base 
{ 
    template<typename U> 
    friend void f(void *ptr); 
    protected: 
    virtual void run() = 0; 
}; 


template<typename U> 
void f(void *ptr) { 
    static_cast<Base<U>*>(ptr)->run(); 
} 

class A : public Base<A> 
{ 
protected: 
    virtual void run() {} 
}; 

class B : public Base<B> 
{ 
protected: 
    virtual void run() {} 
}; 

int main() { 
} 

Приведенный выше код компилируется с моей г ++

+0

Я знаю, что это работает (я уже пробовал это), и я сказал это в своем посте, что я не хочу этого делать. – Nawaz

+0

С помощью ограничений, которые вы предоставили, я думаю, что вы должны это сделать по той причине, что я объяснил в первом абзаце. – CygnusX1

Смежные вопросы