Я пытаюсь написать программу, которая рисует элементы пользовательского интерфейса на ЖК-дисплее для встроенной системы Linux. У меня есть базовый класс под названием Widget, который реализует базовые функции, такие как рисование и стирание, и имеет такие атрибуты, как положение x / y и ширина / высота. Затем я создал производные классы Label, Button и ProgressBar, которые добавляют свои собственные атрибуты (текст, фокус и значение соответственно).

Моя проблема в том, что я хочу создать класс контейнера шаблона под названием WidgetArray, который будет автоматически генерировать сетку виджетов и массово редактировать их свойства. Однако, если WidgetArray содержит, например, функцию, которая принимает вектор строк и назначает каждую строку свойству text объекта Label, если я объявляю экземпляр WidgetArray, содержащие ProgressBar (у которых нет текстового атрибута), компилятор жалуется. Кажется, это идеальный случай для специализации шаблонов. Я читал страницу о специализации шаблонов на cplusplus.com и увидел следующее:

Когда мы объявляем специализации для шаблонного класса, мы также должны определить все его члены, даже те, которые точно равны универсальному шаблонному классу, потому что нет «наследования» членов от универсального шаблона к специализации.

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

Вот несколько упрощенная версия кода, который у меня есть:

template<class T>
class WidgetArray {
protected:
    std::vector<T> children;
    int columns, x, y;
    int element_w, element_h;
    int x_padding, y_padding; 
public:
    WidgetArray() {
        columns = 1;
        children.resize(1);
        x = 0;
        y = 0;
    }
    WidgetArray(int x_in, int y_in, int size, int cols) {
        unsigned int counter1=0, counter2=0;
        columns = cols;
        x = x_in;
        y = y_in;
        element_w = 10;
        element_h = 10;
        x_padding = 0;
        y_padding = 0;
        children.resize(size);
        for (unsigned ii = 0; ii < size; ii++) {
            if (counter1 > columns) {
                counter1 = 0; //column number
                counter2++; //row number
            }
            children.at(ii).x = counter1*(element_w + x_padding) + x;
            children.at(ii).y = counter2*(element_h + y_padding) + y;
            children.at(ii).width = element_w;
            children.at(ii).height = element_h;
        }
    }        
    void draw_children(std::vector<unsigned char>& cmd_str){
        for (unsigned ii=0; ii < children.size(); ii++) children.at(ii).draw(cmd_str);
    }
    void redraw_children(std::vector<unsigned char>& cmd_str) {
        for (unsigned ii=0; ii < children.size(); ii++) {
            children.at(ii).erase(cmd_str);
            children.at(ii).draw(cmd_str);
        }
    }
    void erase_children(std::vector<unsigned char>& cmd_str) {
        for (unsigned ii=0; ii < children.size(); ii++) children.at(ii).erase(cmd_str);
    }
    int reform(int num_elements, int cols, int x_in, int y_in) {
        unsigned int counter1=0, counter2=0;

        children.clear();
        children.resize(num_elements);
        columns = cols;
        x = x_in;
        y = y_in;
        for (unsigned ii = 0; ii < children.size(); ii++) {
            if (counter1 > columns) {
                counter1 = 0; //column number
                counter2++; //row number
            }
            children.at(ii).x = counter1*(element_w + x_padding) + x;
            children.at(ii).y = counter2*(element_h + y_padding) + y;
            children.at(ii).width = element_w;
            children.at(ii).height = element_h;
        }
        return 1;
    }
    int assign_text(std::vector<string> text) {
        for (unsigned ii=0; ii<children.size() || ii < text.size(); ii++) {
            //This is only valid if my type is a Button or Label but I want
            //the rest of the code for WidgetArray to be valid for any derived
            //class of Widget
            children.at(ii).text = text.at(ii);
        }
    }
};

Надеюсь, мой вопрос ясен. Я огляделся и нашел много сообщений о специализации шаблонов, но не нашел ни одного, которое, казалось бы, отвечало на мой вопрос.

1
Beezum 18 Мар 2013 в 04:34
1
Я пытаюсь понять, почему вы вообще специализируете описываемые случаи. Добавленные виджеты должны соответствовать тому, что / каким образом WidgetArray обрабатывает их, чтобы быть уверенным. Все, что выходит за рамки этого, можно расширить в производных, например class ProgressBarArray : public WidgetArray<ProgressBar>, и добавить методы для индикаторов выполнения в тот класс; не по специализации? Или я совершенно неправильно понял вопрос и конечную цель? Другими словами, я не уверен, почему вы думаете, что специализация - правильный ответ?
 – 
WhozCraig
18 Мар 2013 в 04:45
Что ж, теперь я чувствую себя тупым. Это сработает. Думаю, я просто подумал, что, поскольку я использовал шаблоны, это должно быть решение.
 – 
Beezum
18 Мар 2013 в 04:49

1 ответ

Лучший ответ

Похоже, что вам нужен просто шаблонный базовый класс, содержащий все методы, специфичные для обоих, от которых вы затем наследуете. Например:

template <typename T>
struct base
{
protected:
    std::vector<T> children;
    ~base() { }

public:
    void draw_children();
    //Other methods common to all template parameters T

};


class WidgetArrayString
    : public base<std::string>
{
    //Methods specific only to strings
};

class WidgetArrayProgressBar
    : public base<ProgressBar>
{
    //Methods specific only to progress bar
};
2
Yuushi 18 Мар 2013 в 04:49
Спасибо, я не понимал, что вы можете унаследовать определенный тип шаблона, подобный этому.
 – 
Beezum
18 Мар 2013 в 04:52
Если это так, то CRTP (Любопытно повторяющийся шаблон шаблона действительно должен испечь вашу лапшу.
 – 
WhozCraig
18 Мар 2013 в 04:56