Получение реализации чистой виртуальной функции

Рассмотрим следующий пример

#include <iostream>

struct PureVirtual {
    virtual void Function() = 0;
};

struct FunctionImpl {
    virtual void Function() {
        std::cout << "FunctionImpl::Function()" << std::endl;
    }   
};

struct NonPureVirtual : public FunctionImpl, public PureVirtual {
    using FunctionImpl::Function;
};

int main() {
    NonPureVirtual c;
    c.Function();
}

Компилятор (GCC 4.9, Clang 3.5) выходит с ошибкой

test.cpp:18:20: error: variable type 'NonPureVirtual' is an abstract class
    NonPureVirtual c;
                   ^
test.cpp:4:18: note: unimplemented pure virtual method 'Function' in 'NonPureVirtual'
    virtual void Function() = 0;
                 ^

Но когда я не получаю форму PureVirtual, все в порядке. Это странно, потому что стандарт 10.4.4 говорит

Класс является абстрактным, если он содержит или наследует хотя бы один чистый виртуальная функция, для которой конечный переопределитель является чисто виртуальным.

Они ничего не говорят о том, что является окончательным, но я полагаю, что это должно быть FunctionImpl::Function(), особенно когда я сделал его доступным с помощью директивы using. Итак, почему все еще NonPureVirtual абстрактный класс и как я могу исправить это.

Ответ 1

FunctionImpl::Function и PureVirtual::Function - разные функции из разных классов.

Их соответствующие типы void (FunctionImpl::*)() и void (PureVirtual::*)().
Поскольку PureVirtual и FunctionImpl не связаны между собой классы, эти типы функций не связаны.

Они имеют одно и то же имя и одинаковые параметры и тип возвращаемого значения, но поскольку они различны, строка using FunctionImpl::Function не делает эту функцию переопределением той, что находится в PureVirtual.

И если вы объявили переменную типа void (PureVirtual::*)(), вы не смогли бы присвоить ей FunctionImpl::Function.

Другими словами, окончательное переопределение PureVirtual::Function является исходным в PureVirtual, которое является чисто виртуальным.

Для того, чтобы сделать то, что вы хотите, Маттиу М. ответить (используя переадресацию) - это путь, кажется.

Ответ 2

Вы не можете использовать директиву using и вместо этого должны прибегать к следующим вызовам:

struct NonPureVirtual : public FunctionImpl, public PureVirtual {
    virtual void Function() override {
        return FunctionImpl::Function();
    }
};

И да, работает как ожидается.

Ответ 3

Вы приписываете использование объявления с чем-то, чего он не делает. Что он делает (из проекта n3337, 7.3.3/1):

... using-declaration вводит имя в декларативный регион, в котором появляется декларация использования.

Позже из того же абзаца:

Если декларация using называет конструктор (3.4.3.1), он неявно объявляет набор конструкторов в класс, в котором появляется декларация использования (12.9); , иначе имя, указанное в декларации использования, является синоним имени какого-либо объекта, объявленного в другом месте.

В вашем случае FunctionImpl::Function сначала объявляется в классе FunctionImpl и является его членом. Использование-декларация не изменяет этот факт, поскольку в пункте 16 говорится далее:

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

Теперь к определению переопределения (10.3/2):

Если виртуальная функция-член vf объявлена ​​в классе Base и в классе Derived, полученном прямо или косвенно из Base, функция-член vf с тем же именем, тип параметра -list (8.3.5), cv-qualification и ref-quali fi (или отсутствие того же), что и Base::vf, тогда Derived::vf также является виртуальным (независимо от того, объявлен он или нет), и он переопределяет Base::vf. Для удобства мы говорим, что любая виртуальная функция переопределяет себя.

И там также определение окончательного переопределения в том же абзаце.

Функция виртуального члена C::vf объекта класса S является конечным переопределением, если только самый производный класс (1.8), из которого S является подобъектом базового класса (если он есть), объявляет или наследует другую функцию-член, переопределяет vf. В производном классе, если функция виртуального члена субобъекта базового класса имеет более одного конечного переопределения, программа плохо сформирована. [...]

Я думаю, это дает понять, что вы не можете использовать декларацию using для переопределения виртуальной функции. Возможное исправление находится в Ответ Матти.