Шаблонный полиморфизм C++

Я думаю, вы недопонимаете дженерики. Если операция, которую вы пытаетесь выполнить, хороша только для определенных типов данных, тогда вы не выполняете что-то «generic».

Кроме того, поскольку вы хотите, чтобы функция работала только с типами данных int вам не нужна отдельная функция для каждого конкретного размера. Простое использование параметра в самом большом конкретном типе позволит программе автоматически подгонять к нему более мелкие типы данных. (т. е. передача Int16 будет автоматически конвертировать в Int64 при вызове).

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

В противном случае можно использовать параметр типа Object, а затем вам нужно будет проверить тип параметра и предпринять соответствующие действия или выбросить исключение.

29
задан Rusty Horse 4 February 2010 в 11:34
поделиться

5 ответов

Я думаю, что точная терминология для того, что вам нужно, - это «ковариация шаблона», означающая, что если B наследуется от A, то каким-то образом T наследуется от T . Это не относится ни к C ++, ни к универсальным шаблонам Java и C # *.

Есть веская причина избегать ковариации шаблона: это просто удалит всю безопасность типов в классе шаблона. Позвольте мне пояснить следующий пример:

//Assume the following class hierarchy
class Fruit {...};

class Apple : public Fruit {...};

class Orange : public Fruit {...};

//Now I will use these types to instantiate a class template, namely std::vector
int main()
{
    std::vector<Apple> apple_vec;
    apple_vec.push_back(Apple()); //no problem here

    //If templates were covariant, the following would be legal
    std::vector<Fruit> & fruit_vec = apple_vec;

    //push_back would expect a Fruit, so I could pass it an Orange
    fruit_vec.push_back(Orange()); 

    //Oh no! I just added an orange in my apple basket!
}

Следовательно, вы должны рассматривать T и T как совершенно не связанные типы, независимо от отношения между A и B

Итак, как вы могли бы решить проблему, с которой столкнулись? В Java и C # вы можете использовать соответственно ограниченные подстановочные знаки и ограничения :

//Java code
Bar(Container<? extends Interface) {...}

//C# code
Bar<T>(Container<T> container) where T : Interface {...}

Следующий стандарт C ++ (известный как C ++ 1x (ранее C ++ 0x)) изначально содержал еще более мощный механизм под названием Concepts , который позволил бы разработчикам применять синтаксические и / или семантические требования к параметрам шаблона, но, к сожалению, был отложен на более поздний срок. Однако в Boost есть библиотека Concept Check , которая может вас заинтересовать.

Тем не менее, концепции могут быть немного излишними для проблемы, с которой вы сталкиваетесь, использование простого статического утверждения, предложенного @gf , вероятно, является лучшим решением.

* Обновление: Начиная с .Net Framework 4, можно отмечать общие параметры как ковариантные или контравариантные .

39
ответ дан 28 November 2019 в 01:24
поделиться

Можно создать дерево наследования для контейнеров, отражающее дерево наследования данных. Если у вас есть следующие данные:

class Interface {
public:
    virtual ~Interface()
        {}
    virtual void print() = 0;
};

class Number : public Interface {
public:
    Number(int value) : x( value )
        {}
    int get() const
        { return x; }
    void print()
        { std::printf( "%d\n", get() ); };
private:
    int x;
};

class String : public Interface {
public:
    String(const std::string & value) : x( value )
        {}
    const std::string &get() const
        { return x; }
    void print()
        { std::printf( "%s\n", get().c_str() ); }
private:
    std::string x;
};

У вас также могут быть следующие контейнеры:

class GenericContainer {
public:
    GenericContainer()
        {}
    ~GenericContainer()
        { v.clear(); }

    virtual void add(Interface &obj)
        { v.push_back( &obj ); }
    Interface &get(unsigned int i)
        { return *v[ i ]; }
    unsigned int size() const
        { return v.size(); }
private:
    std::vector<Interface *> v;
};

class NumericContainer : public GenericContainer {
public:
    virtual void add(Number &obj)
        { GenericContainer::add( obj ); }
    Number &get(unsigned int i)
        { return (Number &) GenericContainer::get( i ); }
};

class TextContainer : public GenericContainer {
public:
    virtual void add(String &obj)
        { GenericContainer::add( obj ); }
    String &get(unsigned int i)
        { return (String &) GenericContainer::get( i ); }
};

Это не самый эффективный код; это просто, чтобы дать идею. Единственная проблема с этим подходом состоит в том, что каждый раз, когда вы добавляете новый класс данных, вы также должны создавать новый контейнер. Кроме того, у вас есть полиморфизм "снова работает". Вы можете быть конкретным или общим:

void print(GenericContainer & x)
{
    for(unsigned int i = 0; i < x.size(); ++i) {
        x.get( i ).print();
    }
}

void printNumbers(NumericContainer & x)
{
    for(unsigned int i = 0; i < x.size(); ++i) {
        printf( "Number: " );
        x.get( i ).print();
    }
}

int main()
{
    TextContainer strContainer;
    NumericContainer numContainer;
    Number n( 345 );
    String s( "Hello" );

    numContainer.add( n );
    strContainer.add( s );

    print( strContainer );
    print( numContainer );
    printNumbers( numContainer );
}
2
ответ дан Baltasarq 20 November 2019 в 00:42
поделиться

контейнер - это контейнер объектов Foo, а не контейнер объектов интерфейса.

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

, вам нужно

 container<Interface*>

или лучше

 container<shared_ptr<Interface> >
-1
ответ дан pm100 20 November 2019 в 00:42
поделиться

Попробуйте явно вернуть SQL% ROWCOUNT.

Согласно MSDN, DbCommand.. ExecureNonQuery всегда возвращает -1 для вызовов хранимой процедуры:

Для инструкций UPDATE, INSERT и DELETE возвращаемое значение является количеством строк, на которые влияет команда. Для всех остальных типов операторов возвращаемое значение равно -1.

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

-121--4667695-

Требуется json_encode ($ a, JSON_FORCE_OBJECT). К сожалению, он добавлен только в 5.3.

-121--3747424-

No. Представьте, что параметр контейнера «жестко закодирован» в класс, который он определяет (и именно так он работает). Следовательно, тип контейнера - Container _ Foo , несовместимый с Container _ Interface .

Однако вы можете попробовать следующее:

template<class T>
Bar(const Container<T> & bar){
...
}

Тем не менее вы теряете прямой тип проверки таким образом.

На самом деле способ STL (вероятно, более эффективный и общий) будет делать

template<class InputIterator>
Bar(InputIterator begin, InputIterator end){
...
}

... но я предполагаю, что у вас нет итераторов, реализованных в контейнере.

5
ответ дан 28 November 2019 в 01:24
поделиться

Здесь есть две проблемы: конструкции по умолчанию имеют вид MyClass c; ; в круглых скобках это выглядит как объявление функции компилятору.

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

Bar::Bar(const Container<Interface*>&) {}

Container<Interface*> container;
container.push_back(new Foo);
Bar* temp = new Bar(container);

Или конечно, вы можете сделать Bar или его конструктор шаблоном, как показал Корнель.

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

template<class T>
Bar::Bar(const Container<T>& c) {
    BOOST_STATIC_ASSERT((boost::is_base_of<Interface, T>::value));
    // ... will give a compile time error if T doesn't 
    // inherit from Interface
}
11
ответ дан 28 November 2019 в 01:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: