2015-03-16 7 views
0

У этого есть ощущение полного вопроса новичков, но почему следующий код не компилируется, когда спецификатор final используется для B::operator()?C++ одно и то же имя для не виртуальной функции в конфликтах с производным классом с опцией `final`

struct A 
{ 
    virtual void operator()() const = 0; 
}; 

// the CRTP-component is not really necessary here 
// but it possibly makes more sense in that it could be applied like this in reality 
// 
template<typename Derived> 
struct B : A 
{ 
    virtual void operator()() const override final 
    { 
     static_cast<Derived const&>(*this).operator()(); 
    } 
}; 

struct C : B<C> 
{ 
    void operator()() const 
    { 
     //do something 
    } 
}; 

int main() 
{ 
    C()(); 
} 

G ++ печатает следующее error message:

main.cpp:17:14: error: virtual function 'virtual void C::operator()() const' 
     void operator()() const 
      ^
main.cpp:9:22: error: overriding final function 'void B<Derived>::operator()() const [with Derived = C]' 
     virtual void operator()() const override final 
        ^

я бы подумал, что это работает как невиртуальном C::operator() не отменяет виртуальные функции в своих базовых классов? Как я могу заставить это работать (- без изменения имени C::operator())?


EDIT: Как было отмечено несколькими пользователями, то ответ просто, что virtual -ключевое слово в производном классе является излишним (в то время как я думал, оставляя это предотвратит наследование). Тем не менее, цель, которую я должен был спрашивать это - именно последовательный интерфейс по всей динамической и статической иерархии наследования - может быть решена с помощью невиртуальный operator[] повсюду и пару классов A и B виртуальной функции apply:

struct A 
{ 
    void operator()() const 
    { 
     this->apply(); 
    } 

protected: 
    virtual void apply() const = 0; 
}; 

template<typename Derived> 
struct B : A 
{ 
    void operator()() const 
    { 
     static_cast<Derived const&>(*this).operator()(); 
    } 

protected: 
    virtual void apply() const override final 
    { 
     this->operator()(); 
    } 
}; 

struct C : B<C> 
{ 
    void operator()() const 
    { 
     //do something 
    } 
}; 

int main() 
{ 
    C()(); 
} 
+0

@ Cheersandhth.-Alf: Я думал, что 'C :: operator()' только переопределяет, когда используется 'virtual'-ключевое слово ... что неправильно, как я узнал сегодня. – davidhigh

+0

О, ладно. Я изменил свой ответ, убрав вопрос об этом. Думаю, теперь вопрос заключается в том, как кто-то мог узнать о ссылках и 'final' и' override', 'virtual' и' template', а не о переопределениях. –

ответ

4

Функция в производном классе с той же сигнатурой, что и виртуальная функция в базовом классе, переопределяет эту виртуальную функцию из базового класса. Это делает его виртуальной функцией, даже если декларация в производном классе не использует ключевое слово virtual.

Это не может быть изменено, поэтому если вы действительно должны иметь функцию с тем же именем в производном классе, которая не переопределяет виртуальную функцию из базового класса (и в процессе становится виртуальной сам и в этом случае, нарушите final в B), вам нужно будет изменить подпись функции в производном классе. Это может означать другое имя, список различных параметров или разные квалификаторы. Я бы рассмотрел последние два с extreme осторожно, хотя - компилятор сможет разобраться в беспорядке, который вы сделали, но многие читатели могут (очень легко) удивляться.

Если бы я рассматривал такой код, я бы назвал это проблемой, и автору нужно было бы дать очень веские аргументы в пользу того, почему было действительно необходимо, чтобы получить его одобрение.

+0

Спасибо. Я согласен, изменение параметров и квалификаторов - беспорядок. Тем не менее, нет возможности предотвратить переопределение (возможно, просто отбрасывая «виртуальный», как я неправильно думал) является немного неудачным imo, так как это означает, что нельзя смешивать статический и динамический полиморфизм с использованием общих сигнатур функций. Но это может быть субъективным, поскольку я использую этот шаблон очень часто (и теперь, надеюсь, более корректным: -D) – davidhigh

+0

Мое последнее утверждение неверно. Можно использовать не виртуальный 'operator []' всюду и связать динамически связанные ase и производные классы с помощью функции apply. Я делаю редактирование в своем первоначальном вопросе. – davidhigh

+0

@ davidhigh: Ты * можешь * сделать это, но это поражает меня как дизайнерский запах. Выведенные операторы скрывают базовый оператор, а не переопределяют его, поэтому, если (например) вы вызываете с помощью указателя на базу, вы получите другое поведение, чем вы действительно хотите. В целом, наследование говорит, что «A' C' является« A' », но теперь вы возвращаетесь и говорите« но не * действительно »a' A'. –

6

Если функция объявлена ​​virtual в базовом классе, то функция, объявленная с тем же именем и списком параметров неявно virtual в производных классах, используют ли вы в virtual ключевое слово. Вы не можете сделать C::operator()() не виртуальным.

1

В качестве переопределения (поскольку он имеет ту же подпись, что и виртуальная функция в базовом классе), переопределение конфликтует с final, указанным в его базовом классе.

Одно исправление (или, скорее, обходное решение) - предоставить этой функции аргумент по умолчанию, так что он имеет другой тип и, следовательно, не является переопределяющим, и лучший подход - это исправить дизайн.