2010-11-11 2 views
7

Рассмотрим:Переопределение виртуального метода, отличного от const, скрывает перегрузку константы?

#include <iostream> 

using namespace std; 

struct A { 
    virtual void f() { cout << "A::f" << endl; } 
    virtual void f() const { cout << "A::f const" << endl; } 
}; 

struct B : public A {}; 

struct C : public A { 
    virtual void f() { cout << "C::f" << endl; } 
}; 


int main() 
{ 
    const B b; 
    b.f(); // prints "A::f const" 

    const C c; 
    c.f(); 
    // Compile-time error: passing ‘const C’ as ‘this’ argument of 
    // ‘virtual void C::f()’ discards qualifiers 
} 

(. Я использую GCC)

Таким образом, кажется, что сопзИте версию F() получает скрытый в C. Это делает много смысла для меня, но он санкционирован стандартом?

+4

«Виртуальный» - это красная сельдь. Мы не вызываем никаких 'f' фактически (через указатель базового класса или ссылку) здесь. Все поиски 'f' находят наиболее производные' f'. – MSalters

+0

Виртуальные и константы на самом деле не применимы к вопросу, но я оставил их как теги, так как я не вижу большого вреда и не нуждаюсь в добавлении более релевантного тега. – 2010-11-11 09:53:16

+2

Я согласен с 'virtual', но' const' - вот о чем весь вопрос. Переопределение 'f()' скрывает 'f() const'. – Ari

ответ

4

Я буду (еще раз) связывают это большой article:

Во-первых, [составитель] выглядит в непосредственной области , в этом случае объем класса C, и составляет список все функции, которые могут найти, это с именем f (независимо от того, являются ли они доступными или даже принимают количество параметров). Только если он не делает это затем продолжить «наружу» в следующий ограждающих сферы [...]

Так что да, версия fconst скрыта, и это совершенно нормально. Как отметил Симоне, вы можете использовать заявление using, чтобы довести A::f до C.

+0

+1 Хорошая статья. Как интересно, это также обсуждается в пункте 33 эффективного C++ третьего издания –

2

Вставка using B::f;

struct C : public A { 
    using A::f; 
    virtual void f() { cout << "C::f" << endl; } 
}; 

С ++ Стандарт 2003. 13.2 ч.1:

Две функциональные заявления одного и того же названия относятся к одной и той же функции , если они находятся в том же объеме и имеют эквивалентные объявления параметров (13.1). Функция член производного класса не является в той же областью действия как одноименный элемент функции в базовом классе.

Таким образом, C::f скрывает все A::f.

+2

Thid не компилируется. Может быть, вы имеете в виду «A :: f». – Simone

+0

Извините. Конечно, A :: f. –

3

Да, это так. Вы можете написать:

struct C : public A { 
    virtual void f() { cout << "C::f" << endl; } 
    using A::f;  
}; 

, чтобы сделать код компиляцию:

int main() 
{ 
    const B b; 
    b.f(); // prints "A::f const" 

    const C c; 
    c.f(); // prints "A::f const" 
} 

Для более подробной информации, вы можете обратиться к 2010 C++ проект документирует (который вы можете найти here) Глава 10.2 (3-. 4).

+0

+1 для ссылки на стандарт, но я получил больше информации из ответа icecrime. Я хорошо знал опцию «use». Я не был заинтересован в компиляции, а в понимании языка. – Ari

+0

Спасибо, Ари, но, пожалуйста, помните, что с вашим вопросом можно обратиться к той же проблеме, которая также хочет скомпилировать ее. Лучше написать что-то большее, чем что-то меньшее, согласны ли вы? :) – Simone

3

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

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

struct Base { 
}; 

struct Derived : Base { 
    void f(double); 
} 

void g(Derived &d) { 
    d.f(42); 
} 

Затем вам нужно изменить базу, чтобы включить метод, который делает что-то совершенно другое, но, для какая-то причина, вы хотите назвать его «F»:

struct Base { 
    void f(int); 
}; 

Без этого правила, каждые использования Derived вызывающих е необходимо оценить вручную - и если база находится в библиотеке данные других людей, вы даже не можете ccess для других целей! Это становится хуже перед лицом пользовательских (неявных) конверсий.

Вместо этого было решено потребовать, чтобы производные классы явно указывали, что они хотят импортировать данные из базы с помощью объявления using. Это правило может быть неожиданным, и я не уверен, что сегодня это чистая польза для языка, но они не спрашивали меня - в то время я, вероятно, мог бы ответить только на них словами с двумя слогами. :)