вы можете сделать что-то вроде этого:
describe 'validations' do
let(:some_variable_object) { SomeVariable.new }
let(:new_foo) { described_class.new(some_variable: some_variable_object) }
context 'when some_variable is valid' do
before do
allow(some_variable_object).to receive(:valid?) { true }
end
it 'is valid' do
expect(new_foo).to be_valid
end
it 'does not have errors related to some_variable' do
expect(new_foo.errors[:some_variables]).to be_empty
end
end
тогда вы можете сделать то же самое, чтобы проверить обратное, когда some_variable
недействительно ... сейчас есть инструменты, которые помогут вам настроить объекты в спецификации легко (FactoryBot).
Ваш пример с помощью Повышения. Вариант и посетитель:
#include <string>
#include <list>
#include <boost/variant.hpp>
#include <boost/foreach.hpp>
using namespace std;
using namespace boost;
typedef variant<string, int, bool> object;
struct vis : public static_visitor<>
{
void operator() (string s) const { /* do string stuff */ }
void operator() (int i) const { /* do int stuff */ }
void operator() (bool b) const { /* do bool stuff */ }
};
int main()
{
list<object> List;
List.push_back("Hello World!");
List.push_back(7);
List.push_back(true);
BOOST_FOREACH (object& o, List) {
apply_visitor(vis(), o);
}
return 0;
}
Одна хорошая вещь об использовании этой техники состоит в том, что, если, позже, Вы добавляете другой тип к варианту и Вы забываете изменять посетителя для включения того типа, это не скомпилирует. Необходимо поддерживать каждый возможный случай. Принимая во внимание, что, если Вы используете переключатель или расположение каскадом, если операторы, легко забыть вносить изменение везде и представлять ошибку.
C++ не поддерживает неоднородные контейнеры.
Если Вы не собираетесь использовать boost
взлом должен создать фиктивный класс и иметь все различные классы, происходят из этого фиктивного класса. Создайте контейнер по Вашему выбору для содержания фиктивных объектов класса, и Вы готовы пойти.
class Dummy {
virtual void whoami() = 0;
};
class Lizard : public Dummy {
virtual void whoami() { std::cout << "I'm a lizard!\n"; }
};
class Transporter : public Dummy {
virtual void whoami() { std::cout << "I'm Jason Statham!\n"; }
};
int main() {
std::list<Dummy*> hateList;
hateList.insert(new Transporter());
hateList.insert(new Lizard());
std::for_each(hateList.begin(), hateList.end(),
std::mem_fun(&Dummy::whoami));
// yes, I'm leaking memory, but that's besides the point
}
Если Вы собираетесь использовать boost
можно попробовать boost::any
. Вот пример использования boost::any
.
Можно найти эту превосходную статью двух ведущих экспертов C++ по интересу.
Теперь, boost::variant
другая вещь состоит в том, чтобы высматривать как j_random_hacker упомянутый. Так, вот сравнение для получения справедливой идеи того, что использовать.
С a boost::variant
код выше выглядел бы примерно так:
class Lizard {
void whoami() { std::cout << "I'm a lizard!\n"; }
};
class Transporter {
void whoami() { std::cout << "I'm Jason Statham!\n"; }
};
int main() {
std::vector< boost::variant<Lizard, Transporter> > hateList;
hateList.push_back(Lizard());
hateList.push_back(Transporter());
std::for_each(hateList.begin(), hateList.end(), std::mem_fun(&Dummy::whoami));
}
boost::variant
подобно предложению dirkgently boost::any
, но поддерживает Шаблон "посетитель", означая, что легче добавить определенный для типа код позже. Кроме того, это выделяет значения на стеке вместо того, чтобы использовать динамическое выделение, ведя к немного более эффективному коду.
Править: Поскольку litb указывает в комментариях, с помощью variant
вместо any
средства можно только держать значения из одного из предуказанного списка типов. Это часто - сила, хотя это могла бы быть слабость в случае автора вопроса.
Вот пример (не использующий Шаблон "посетитель" хотя):
#include <vector>
#include <string>
#include <boost/variant.hpp>
using namespace std;
using namespace boost;
...
vector<variant<int, string, bool> > v;
for (int i = 0; i < v.size(); ++i) {
if (int* pi = get<int>(v[i])) {
// Do stuff with *pi
} else if (string* si = get<string>(v[i])) {
// Do stuff with *si
} else if (bool* bi = get<bool>(v[i])) {
// Do stuff with *bi
}
}
(И да, необходимо технически использовать vector<T>::size_type
вместо int
для i
введите, и необходимо технически использовать vector<T>::iterator
вместо этого так или иначе, но я пытаюсь сохранить это простым.)
Как часто такая вещь на самом деле полезна? Я программировал в C++ в течение довольно многих лет, на различных проектах, и на самом деле никогда не хотел неоднородный контейнер. Это может быть распространено в Java по некоторым причинам (у меня есть намного меньше опыта Java), но для любого данного использования его в проекте Java мог бы быть способ сделать что-то другое, что будет работать лучше в C++.
C++ имеет более тяжелый акцент на безопасность типов, чем Java, и это очень небезопасно типом.
Тем не менее, если объекты не имеют ничего общего, почему Вы храните их вместе?
Если у них действительно есть общие черты, можно сделать класс для них для наследования; поочередно, используйте повышение::любой. если они наследовали, имеют виртуальные функции, чтобы назвать, или использовать dynamic_cast <>, если Вы действительно имеете к.
Я был бы точно так же, как, чтобы указать, что использование динамического преобразования типа для ветвления на основе типа часто, намекает на дефекты в архитектуре. Большинство раз можно достигнуть того же эффекта с помощью виртуальных функций:
class MyData
{
public:
// base classes of polymorphic types should have a virtual destructor
virtual ~MyData() {}
// hand off to protected implementation in derived classes
void DoSomething() { this->OnDoSomething(); }
protected:
// abstract, force implementation in derived classes
virtual void OnDoSomething() = 0;
};
class MyIntData : public MyData
{
protected:
// do something to int data
virtual void OnDoSomething() { ... }
private:
int data;
};
class MyComplexData : public MyData
{
protected:
// do something to Complex data
virtual void OnDoSomething() { ... }
private:
Complex data;
};
void main()
{
// alloc data objects
MyData* myData[ 2 ] =
{
new MyIntData()
, new MyComplexData()
};
// process data objects
for ( int i = 0; i < 2; ++i ) // for each data object
{
myData[ i ]->DoSomething(); // no type cast needed
}
// delete data objects
delete myData[0];
delete myData[1];
};
Печально нет никакого простого способа сделать это в C++. Необходимо создать базовый класс сами и получить все другие классы из этого класса. Создайте вектор указателей базового класса и затем используйте dynamic_cast (который идет с его собственным временем выполнения наверху) найти фактический тип.
Только для полноты этой темы я хочу упомянуть, что можно на самом деле сделать это с чистым C при помощи пустоты* и затем кастинг ее в то, чем это должно быть (хорошо, моим примером не является чистый C, так как это использует векторы, но это сохраняет меня некоторый код). Это будет работать, если Вы будете знать то, что вводит Ваши объекты, или если Вы храните поле где-нибудь, которое помнит это. Вы несомненно не хотите делать это, но вот пример, чтобы показать, что это возможно:
#include <iostream>
#include <vector>
using namespace std;
int main() {
int a = 4;
string str = "hello";
vector<void*> list;
list.push_back( (void*) &a );
list.push_back( (void*) &str );
cout << * (int*) list[0] << "\t" << * (string*) list[1] << endl;
return 0;
}
Я - довольно неопытное, но здесь - то, что я пошел бы с -
Я уверен, что намного лучшее решение возможно. Я также уверен, что лучшее объяснение возможно. Я узнал, что у меня есть некоторые плохие привычки программирования на C++, таким образом, я попытался передать свою идею, не входя в код.
Я надеюсь, что это помогает.
Около факта, поскольку большинство указало, Вы не можете сделать этого, или что еще более важно, более, чем вероятные, Вы действительно не хотите.
Давайте отклоним Ваш пример и рассмотрим что-то ближе к реальному примеру. А именно, некоторый код я видел в реальном проекте с открытым исходным кодом. Это попыталось эмулировать CPU в символьном массиве. Следовательно это поместило бы в массив однобайтовый "код операции", сопровождаемый 0, 1 или 2 байта, которые могли быть символом, целым числом или указателем на строку, на основе кода операции. Для обработки этого это включило большую разрядную игру.
Мое простое решение: 4 отдельных стека <> s: Один для перечисления "кода операции" и один каждый для символов, ints и строки. Возьмите следующее от стопки кода операции, и берет Вас который из других трех для получения операнда.
Существует очень хороший шанс, Ваша фактическая проблема может быть решена похожим способом.
В то время как Вы не можете сохранить типы примитивов в контейнерах, можно создать классы обертки типа примитива, которые будут подобны автопомещенным в коробку типам примитивов Java (в примере, примитивные введенные литералы на самом деле автоупаковываются); экземпляры которого появляются в коде C++ (и может (почти) использоваться) точно так же, как примитивные переменные/элементы данных.
Посмотрите объектные обертки для встроенных типов от структур данных и алгоритмов с объектно-ориентированными шаблонами разработки в C++.
С перенесенным объектом можно использовать идентификатор типа C++ () оператор для сравнения типа. Я вполне уверен, следующее сравнение будет работать: if (typeid(o) == typeid(Int))
[где Интервал был бы перенесенным классом для международного типа примитива, и т.д....] (иначе просто добавляют функцию к Вашим примитивным оберткам, которая возвращает идентификатор типа и таким образом: if (o.get_typeid() == typeid(Int))
...
Однако относительно Вашего примера это имеет запах кода мне. Если это не единственное место, где Вы проверяете тип объекта, я был бы склонен использовать полиморфизм (особенно, если у Вас есть другие методы/функции, конкретные относительно типа). В этом случае я использовал бы примитивные обертки, добавляющие класс, с которым соединяют интерфейсом, объявляя задержанный метод (для того, чтобы сделать, 'действительно наполняют'), который был бы реализован каждым из Ваших перенесенных примитивных классов. С этим Вы смогли бы использовать свой контейнерный итератор и устранить Ваш, если оператор (снова, если бы у Вас только есть это сравнение типа, настраивая задержанный метод с помощью полиморфизма только для этого, было бы излишество).
RTTI (Информация о типе выполнения) в C++ всегда был жесток, особенно кросс-компилятор.
Вы - наилучший вариант, должен использовать STL и определить интерфейс для определения типа объекта:
public class IThing
{
virtual bool isA(const char* typeName);
}
void myFunc()
{
std::vector<IThing> things;
// ...
things.add(new FrogThing());
things.add(new LizardThing());
// ...
for (int i = 0; i < things.length(); i++)
{
IThing* pThing = things[i];
if (pThing->isA("lizard"))
{
// do this
}
// etc
}
}
Mike
Короткий ответ..., Вы не можете.
Длинный ответ..., необходимо было бы определить собственную новую иерархию объектов, которые все наследовали от базового объекта. В Java все объекты в конечном счете убывают от "Объекта", который является тем, что позволяет Вам делать это.
Ну, Вы могли создать базовый класс и затем создать классы, которые наследовались ему. Затем сохраните их в станд.:: вектор.