Disclaimer: I cannot tell for the OP, there are various possibilities dependent on the actual use case, coding conventions and needs.
Below I am presenting my subjective selection to what I feel should be said in the context of the question. Again, this list is not exhaustive by any means.
To the point, in my opinion the main possibilities worth mentioning here are:
- Making some factory method that returns the Settings class.
- Using aggreate initialization.
- Utilizing the builder pattern.
The main catch with your code is the fact, that Settings
has only default constructor. In such situation, you have to default-construct it somewhere and then pass it.
#include <string>
class Foo {
public:
struct Settings {
int opt_1;
int opt_2;
std::string opt_3;
bool opt_4;
Settings() : opt_1(0), opt_2(100), opt_3(""), opt_4(true) {}
};
Foo (const Settings &settings = Settings());
};
struct BabyFoo : public Foo
{
static Foo::Settings makeFooSettings()
{
Foo::Settings retval;
retval.opt_1=1;
retval.opt_2=22;
retval.opt_3="abcd";
retval.opt_4=false;
return retval;
}
BabyFoo() : Foo(makeFooSettings()){}
};
Without the default constructor, the aggreate initialization can be used:
Does it defeat the purpose? I don't know, you tell me that ;)
With aggreate initialization last arguments can be easily omitted:
#include <string>
using namespace std::literals::string_literals;
class Foo {
public:
struct Settings
{
int opt_1 {0};
int opt_2 {100};
std::string opt_3{"abcd"s};
bool opt_4{true};
};
Foo(const Settings& s);
Foo();
};
struct BabyFoo : public Foo
{
BabyFoo() : Foo({8,42}){}
};
(two constructors in Foo due to: Error when using in-class initialization of non-static data member and nested class constructor)
BTW, this gets even better and kinda discards the next options with C++20 designated initializers:
struct BabyFoo : public Foo
{
BabyFoo() : Foo({.opt_4=false}){} //note that this is available since C++20
};
And if you don't like any of those solutions, you can have a look at builder pattern:
#include <string>
using namespace std::literals::string_literals;
class Foo {
public:
struct Settings
{
int opt_1 {0};
int opt_2 {100};
std::string opt_3{"abcd"s};
bool opt_4{true};
class Builder
{
public:
Builder& withOpt1(int o) {o1=o; return *this;}
Builder& withOpt2(int o) {o2=o; return *this;}
Builder& withOpt3(const std::string& o) {o3=o; return *this;}
Builder& withOpt1(bool o) {o4=o; return *this;}
Settings build() const {return Settings{o1,o2,o3,o4};}
private:
int o1{};
int o2{};
std::string o3{};
bool o4{};
};
};
Foo(const Settings& s);
Foo();
};
struct BabyFoo : public Foo
{
BabyFoo() : Foo(Foo::Settings::Builder().withOpt3("abc").build()){}
};
Note, that maybe the Builder should be building directly Foo, not its settings, but again, that up to the context.