2016-04-13 6 views
2

Я знаю, что нельзя иметь виртуальную функцию шаблона-члена, но мне нужно что-то похожее на нее.Функция виртуального шаблона-члена

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

struct abstract 
{ 
    template<typename T> 
    virtual T get() const = 0; 
}; 

using abstract_pointer = std::shared_ptr<abstract>; 

struct concrete_int : public abstract 
{ 
    template<> 
    int get() const { return 123; } 
}; 

struct concrete_string : public abstract 
{ 
    template<> 
    std::string get() const { return "abc"; } 
}; 

abstract_pointer factory() 
{ 
    // Some logic here to decide what concrete type to return 
    return ...; 
} 

void print_value(abstract_pointer p) 
{ 
    // Will print either 123 or "abc" 
    std::cout << "p = " << p->get() << '\n'; 
} 

int main() 
{ 
    abstract_pointer p = factory(); 

    print_value(p); 
} 

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

Его можно легко решить с помощью CRTP и вывода типа, но тогда невозможно передать объект другим функциям, как в приведенном выше примере.

Я мог бы также использовать вариант Boost или союзы, но тогда он может быстро стать громоздким, если добавить более конкретные классы. Я мог бы также использовать Boost, но тогда мне пришлось бы использовать any_cast, и это было бы не так ... ну, nice и просто.

Может быть, сегодня мой google-fu плохой, или я просто слишком устал, но я действительно не нашел никакого способа обойти его. Можно ли сделать что-то вроде этого, сохраняя при этом его достаточно гибким, чтобы добавлять более конкретные классы, а также поддерживать интерфейс простым и приятным?


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

Причина, по которой я хочу использовать абстрактные/конкретные классы и наследование, заключается в том, что я хочу, чтобы lexer был достаточно гибким, чтобы использоваться несколькими языками, поэтому должно быть легко, например, добавить дочерний токен идентификатора класс для «ключевого слова» и, возможно, даже один конкретный класс для каждого ключевого слова.

Но, возможно, AaronI прав, я стараюсь сделать что-то сложное. Я позволю этому вопросу встать на тот случай, если кто-нибудь придумает хорошее решение или найдет хороший дубликат, тем временем я подумаю об этом и посмотрю, смогу ли я придумать что-то приемлемое.

+1

Всякий раз, когда мне это нужно, я прибегал к CRTP. Однако даже CRTP позволяет иметь абстрактные (не templated) базовые классы, чтобы упростить их передачу. Я использовал этот подход в значительной степени с помощью моей [государственной системы состояний STTCL] (https://github.com/makulik/sttcl). –

+1

Возможно, проверьте Boost.TypeErasure? –

+0

не может абстрактный объект просто поддерживать посещение? Трудно понять это требование, не зная случая использования. –

ответ

0
struct abstract 
{ 
    virtual string get() const = 0; 
}; 

using abstract_pointer = std::shared_ptr<abstract>; 

struct concrete_int : public abstract 
{ 
    string get() const override { return "123"; } 
}; 

struct concrete_string : public abstract 
{ 
    string get() const override { return "abc"; } 
}; 

abstract_pointer factory() 
{ 
    // Some logic here to decide what concrete type to return 
    return ...; 
} 

void print_value(abstract_pointer p) 
{ 
    // Will print either 123 or "abc" 
    std::cout << "p = " << p->get() << '\n'; 
} 

int main() 
{ 
    abstract_pointer p = factory(); 

    print_value(p); 
} 

Easy-peasy. :)

Отказ от ответственности: код не рассматривается компилятором.

+1

Должно ли 'get()' in 'specific_int' возвращать' int' вместо 'string'? – Andy

+0

Легко-peasy действительно, но не * совершенно * то, что я искал ... :) Я буду иметь это в виду как альтернатива, посмотрим, как это получится в конце. :) –

0

Если вы планируете вводить новые ключевые слова в качестве токенов и в то же время использовать универсальный интеллектуальный указатель для доступа ко всем токенам, то возникает необходимость в том, чтобы «token :: get()» возвращал то, что достаточно фундаментально для понимается кодом, который вызывает «токен :: get()». Рассмотрим «токен» здесь как абстрактный класс.

Если вы используете (cout) 'токен через' get() ', и если какой-то конкретный токен возвращает объект, то' cout 'не поймет, что это за объект или как напечатать объект.

В таком случае преобразование всех токенов в какой-то общий фундаментальный тип (скажем, «char *» или «string», как было предложено «Cheers and hth. - Alf»), кажется хорошим.

Кроме того, если печать является одним из требований, то следующая функция может быть добавлена ​​в абстрактном классе:

virtual void token::print(ostream &); 

Таким образом, каждый конкретный объект будет печатать сам.

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