I have a class with an array of scoped pointers to objects which do NOT have a default constructor.
The only way I've found to "initialise" them is using swap() like this:
class Bar {
Bar(char * message) {};
}
class Foo
{
boost::scoped_ptr<Bar> arr[2];
Foo()
{
arr[0].swap(boost::scoped_ptr<Bar>( new Bar("ABC") ));
arr[1].swap(boost::scoped_ptr<Bar>( new Bar("DEF") ));
};
}
This feels a little verbose and clunky. Have I missed a smarter way of doing it?
arr[0].reset(new Bar("ABC"));
arr[1].reset(new Bar("DEF"));
The biggest problem is that there is no way to initialize an array using the member initializer list here.
You might want to use a specialized pointer container like ptr_vector instead:
struct Foo {
boost::ptr_vector<Bar> bars;
X() : bars(boost::assign::ptr_list_of<Bar>("ABC")("CDE")) {}
};
What about using a typedef?
typedef boost::scoped_ptr<Bar> TBarPtr;
arr[0].swap(TBarPtr(new Bar("ABC"));
One solution: use boost::ptr_vector instead of an array.
You could also use std::vector<scoped_ptr> and fill the array using push_back. Edit: I think this won't work with scoped_ptr.
Related
Say we have an enum type foo which we want to use to index an array arr of static size.
If we want to use an enum class for this, we could try it like this:
enum class foo
{
a,
b,
c,
count
};
std::array<T, static_cast<int>(foo::count)> arr;
However, the count field is a hack. Can we obtain the number of fields of foo in a more elegant way?
In any case, what's really bad is that we need to access the array using a static_cast as well: arr[static_cast<int>(foo::a)].
Of course we could write a custom "at" function (see https://www.fluentcpp.com/2019/01/15/indexing-data-structures-with-c-scoped-enums/) or provide an "enum_array" class (see https://stackoverflow.com/a/55259936/547231), but both solutions are somehow complicated and we might better give up and use a simple std::array<T, int> instead ...
However, it's way more intuitive to read arr[foo::a] instead of arr[0], where we always need to remember what's the meaning of the index 0 in the latter.
Can we do better?
No, not really.
There are many proposals that enable static reflection of enum values. None are in C++ yet.
I mean you could do:
namespace foo {
enum value {
a,b,c,count
};
}
then the to int conversion is implicit, and you don't pollute the contained namespace.
The solution here is pretty close to 0 overhead and lets you use enums (and only enums) as keys to [].
So you get:
enum_array<foo, T> arr;
and arr behaves like you want it to.
As a partial solution you can define
constexpr std::underlying_type_t<foo> operator*(foo f) {
return static_cast<std::underlying_type_t<foo>>(f);
}
and then write
int bar(std::array<int, *foo::count>& arr) {
return arr[*foo::b];
}
Demo
I think I might have an oddly specific/weird question that I might be making more complex then it needs to be.
I have a simple class:
class Foo{
public:
struct Bar{
int otherdata;
};
int somedata;
};
In another file I import this class and create an Instance of Foo
I also want to create a vector of type Bar..how would I do this?
I have tried:
//Assume I already imported proper prereqs
int main() {
Foo test;
vector<test::Bar> vec;
//or
vector<test.Bar> vec;
//or
vector<Bar> vec;
}
Whats the best way to do this? I can simply do this if I take the struct and place it outside the class..but is there a way to encapsulate the struct and create a vector of that type of struct. I should point out that I do not want to initialize the vector inside the class, but rather in my main function.
Like this:
vector<Foo::Bar> vec;
You're looking for vector<Foo::Bar>.
The structure/class Bar will always have one int regardless of the instance of Foo, so it makes no sense (or at least it's redundant) to qualify it with test as in test::Bar or test.Bar. Hence, we qualify it as Foo::Bar.
I've got a class:
class Foo {
private:
Other bar;
};
and Other class:
class Other {
public:
Other(std::list<int> l, ...other parameters...);
};
Other doesn't have a default contructor and I can't add it. How can I initialize the attribute bar? I mean I need to create a list, I need to add some item and so on, so it's really difficult to use the initialization list because I need to write code to fill the list. How can I do? I think I can use a pointer instead, but I'd like to avoid dynamic allocation.
You could write a function to create the object, and use that in the initialiser list:
static Other make_bar() {
std::list<int> l;
// fill the list, do whatever else you need
return Other(l, ...);
}
Foo() : bar(make_bar()) {}
Or you could use something like boost::optional to defer initialisation without requiring dynamic allocation.
You can implement a constructor in Foo using:
Foo() : bar(std::list<int>()) {}
So I've got a class that I'm generalizing into a base class. One of the member variables is a 2D array of a struct.
struct SomeType
{
...
}
and then in the class's header:
SomeType member_variable_ [SIZE_ONE][SIZE_TWO];
But, in my situation, SIZE_TWO needs to be set when the class is initialized because it's going to be different depending on what's using this. What's the best way to have a 2D struct array with a size that's not yet set as a member variable?
The simplest way to solve it is to not use C-style arrays at all, but to use std::vector. Or possibly an std::array of vectors:
std::array<std::vector<SomeType>, SIZE_ONE> member_variable_;
Now you can easily insert as many (or as few) SomeType objects as needed, and still use the array-indexing syntax:
member_variable_[some_index][some_other_index]
To set a fixed size at runtime for the "second" Dimension, you can do something like this in the constructor:
for (auto& v : member_variable_)
v = std::vector<SomeType>(the_runtime_size);
You could use a template:
template<unsigned SIZE_TWO>
class theClass
{
SomeType member_variable_ [SIZE_ONE][SIZE_TWO];
SIZE_TWO will be set when you instantiate the class.
theClass<5> tc; //member_variable_ [SIZE_ONE][5];
You could also use containers like std::vector or std::array.
So, in C the standard way is stdarg.h. But I'm looking to pull up something a bit like this:
template<int A>
class MyClass {
public:
MyClass(...) {
// fill each value of array with an argument.
};
virtual ~MyClass() { };
private:
float array[A];
};
Obviously, the idea is not to have different constructions for every possible amount of arguments. Any suggestions, standard ways, whatsoever?
Thanks,
Julian.
In C++11 you can use an std::initializer_list constructor for this kind of scenario. That allows for this type of initialization:
MyClass<5> x{1,2,3,4,5};
although you have to define what happens when the dimensions do not match. But for these kinds of statically sized arrays, it is worth looking at std::array. These have a well defined behaviour when the dimensions of the initializer don't match their own.
you need to use initializer_list
explicit MyClass(std::initializer_list<T> list_args){
// fill each value of array with an argument.
};
If you don't have access to C++11 initializer_lists you can just pass a vector.
MyClass(const std::vector& values) {
//copy values into array
};
MyClass someClass( std::vector<int>(10,4) ); //adds 10x 4 to the vector