Я думаю, вы недопонимаете дженерики. Если операция, которую вы пытаетесь выполнить, хороша только для определенных типов данных, тогда вы не выполняете что-то «generic».
Кроме того, поскольку вы хотите, чтобы функция работала только с типами данных int вам не нужна отдельная функция для каждого конкретного размера. Простое использование параметра в самом большом конкретном типе позволит программе автоматически подгонять к нему более мелкие типы данных. (т. е. передача Int16 будет автоматически конвертировать в Int64 при вызове).
Если вы выполняете разные операции на основе фактического размера int, передаваемого в функцию, тогда я думаю, вам следует серьезно пересмотреть даже попытку делать то, что вы делаете. Если вам нужно обмануть язык, вы должны думать немного больше о том, чего вы пытаетесь выполнить, а не о том, как делать то, что вы хотите.
В противном случае можно использовать параметр типа Object, а затем вам нужно будет проверить тип параметра и предпринять соответствующие действия или выбросить исключение.
Я думаю, что точная терминология для того, что вам нужно, - это «ковариация шаблона», означающая, что если 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, можно отмечать общие параметры как ковариантные или контравариантные .
Можно создать дерево наследования для контейнеров, отражающее дерево наследования данных. Если у вас есть следующие данные:
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 );
}
контейнер - это контейнер объектов Foo, а не контейнер объектов интерфейса.
И он также не может быть полиморфным, могут быть указатели на вещи, но не сами объекты. Насколько большими должны быть слоты в контейнере для контейнера, если бы вы могли поместить в него все, что происходит от интерфейса
, вам нужно
container<Interface*>
или лучше
container<shared_ptr<Interface> >
Попробуйте явно вернуть 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){
...
}
... но я предполагаю, что у вас нет итераторов, реализованных в контейнере.
Здесь есть две проблемы: конструкции по умолчанию имеют вид 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
}