2016-11-29 3 views
2

У меня есть шаблон Base класс, который я хочу наследовать. Класс Base имеет общедоступный метод, который делает допущение о типе шаблона. Упрощенная версия кода приведена ниже:Наследование класса шаблона в C++ с неподдерживаемым типом

#include <iostream> 

using namespace std; 

template <typename V> 
class Base { 
public: 
    virtual void print() { 
    a = "a"; 
    cout << "Base class" ; 
    } 
    void callIt() { 
    print(); 
    } 
    V a; 
}; 

class Derived : public Base<int> { 
public: 
    void print() override { 
    cout << "Derived class\n"; 
    } 
}; 


int main() { 
    Derived d; 
    d.callIt(); 
    return 0; 
} 

Компиляция кода с GCC 6.2.0 дает мне эту ошибку:

test.cpp: In instantiation of 'void Base<V>::print() [with V = int]': 
test.cpp:13:10: required from 'void Base<V>::callIt() [with V = int]' 
test.cpp:28:12: required from here 
test.cpp:9:7: error: invalid conversion from 'const char*' to 'int' [-fpermissive] 
    a = "a"; 
    ~~^~~~~ 

Я хочу, чтобы полностью скрыть реализацию Base класса метода print() из компилятора. Как я могу это сделать?

EDIT: Как ни странно, закомментировать строку a = "a"; позволяет код успешно компилируется и напечатает Derived class.

+0

Извините, почему 'Base :: print' делает предположение о' V', если 'Base' предназначен для наследования более общего шаблона? – Brian

+0

@Brian 'Base' - это часть библиотеки, в которой я не контролирую ее, но мне нужны другие функции, которые она предоставляет. – Javad

+1

Знаете ли вы, что на самом деле весело? Прокомментируйте 'a =" a ";' и посмотрите результат. http://melpon.org/wandbox/permlink/Kci1HFRYtKbtbLk8 (Предупреждение о спойлере: выход «Производный класс». Что?) –

ответ

2

Поскольку ваш print является виртуальным, его необходимо создать при каждом наследовании от Base (поскольку компилятор должен сгенерировать правильный vtable), даже если функция еще не вызывается. Это было бы не так, если бы print был не виртуальным членом - в этом случае он был бы создан только при вызове.

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

+0

Вы правы, но я думаю, что я упростил код. Я только что обновил код. 'print()' вызывается 'Base :: callIt()'. Удаление виртуального в старом коде поможет, но в новом коде это не так. Я попытался прокомментировать 'a =" a ";' и скомпилировал код и получил 'Base class' вместо' Derived class'. Любое решение этой проблемы? – Javad

4

Можно получить код работает, если вы добавите специализацию Base<int>::print() до объявления Derived:

#include <iostream> 

using namespace std; 

template <typename V> 
class Base { 
public: 
    virtual void print() { 
    a = "a"; 
    cout << "Base class" ; 
    } 
    void callIt() { 
    print(); 
    } 
    V a; 
}; 

template <> 
void Base<int>::print() { 
    cout << "Specialization\n"; 
} 

class Derived : public Base<int> { 
public: 
    void print() override { 
    cout << "Derived class\n"; 
    } 
}; 


int main() { 
    Derived d; 
    d.callIt(); 
    return 0; 
} 

Live Demo

Будьте осторожны, делая это, хотя: она нарушает предположения, автор Base сделал о какие типы работают с шаблоном, и могут легко привести к ошибкам, затрудняющим отслеживание в библиотечном коде.

+0

Почему 'Base :: callIt()' необходим? @Javad не может добавить его в 'Base', а вызов' d.print() 'отлично работает: http://melpon.org/wandbox/permlink/7bFxmV88g1izYBbc –

+1

Потому что он находится в коде OP. 'Base :: callIt()' - причина, по которой OP не может отбросить «virtual» квалификатор от 'Base :: print()'. –

+0

О, извините, я этого не видел. Но, честно говоря, он был отредактирован в исходное сообщение после того, как я скопировал код в Wandbox :) –

0

Упрощая изолировать проблему:

template <typename V> 
class Base { 
public: 
    virtual void print() { 
    a = "a"; 
    } 

    V a; 
}; 

int main() 
{ 
    Base<int> b; 
} 

приводит:

<source>:5:7: error: invalid conversion from 'const char*' to 'int' [-fpermissive] 
a = "a"; 

Это происходит потому, что мы, в сущности, говоря:

int a = "a"; 

и, конечно же, что это ошибка, потому что вы не можете назначить const char*int.

Итак, единственный разумный вывод, который следует сделать здесь, состоит в том, что Base<T> был разработан с ограничением, что должно существовать доступное преобразование от const char* до T.

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

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