4

I wondered if there was a new declaration like in C# for C++

C# allows you to do this and it just neatens up the code a bit:

FuncCall( new Foo() {
    Bar = "sausage",
    Boo = 4
} );

It's just I thought this was a bit sloppy in C++:

unique_ptr<Foo> foo( new Foo() );
foo.Bar = "sausage";
foo.Boo = 4;

FuncCall( move( foo ) );

Foo might look like this:

class Foo
{
public:
    Foo();

    string Bar;
    int Boo;
}

Why am i not just placing all into construct paramters?

Because it's stupid when you have to initalize so much:

Foo( int width, int height, string title, string className, string thjis, stihjrjoifger gfirejgoirejgioerjgoire ) It goes on forever... Whereas i already have the properties inside my class... So was just wondering if it could be done..

Jimmyt1988
  • 20,466
  • 41
  • 133
  • 233
  • 1
    Along with uniform initialization. You should look into [std::make_unique](http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique) also. `new` is rarely used in modern C++ code. – Jesse Good May 09 '15 at 22:16
  • That's great, but i don't have C++ 14 available to me to use it in my current app. However, I'd like to know if C++ 14 offers what i requested :) – Jimmyt1988 May 09 '15 at 22:18
  • _"Foo might look like this"_ That's a pretty bad way for it to look. A default constructor only? How do those members get initialised? To what? How does the user of your class operate it? – Lightness Races in Orbit May 09 '15 at 22:19
  • Man, my question isn't about declaring getters and setters buddy, it's asking if you can do a declaration like i requested... Please man... gosh – Jimmyt1988 May 09 '15 at 22:21
  • 1
    @Jimmyt1988: If you cannot use `C++14`, you can [add this one to your project](http://stackoverflow.com/a/13512344/906773) as a utility ;). – Jesse Good May 09 '15 at 22:22
  • You should consider that you must `delete` the allocation, so if `foo` keeps the pointer, and it can be deleted afterward, or delete it, it maybe a good usage, but if not, it called a memory leak. – SHR May 09 '15 at 22:24
  • 2
    If you cannot use C++14 why did you tag your question C++14? – kfsone May 09 '15 at 22:32
  • Because i wanted to know if you could do it in C++... Not specifically the C++ i am stuck with.... Your question however ( make_unique ) does not answer my question, it simply states that i can use a different way of instantiating a new ptr. – Jimmyt1988 May 09 '15 at 22:35
  • P.S i'm sorry for being poopy, and thank you for pointing it out for others if they come in... yes you can avoid using the new keyword in C++ 14.. It's cool. Nice one. – Jimmyt1988 May 09 '15 at 22:37
  • @Jimmyt1988: `Whereas i already have the properties inside my class`. C++ does not have C# properties. Making a member `public` does not make it a property. – Jesse Good May 09 '15 at 22:40
  • Thanks for that insightful information Jesse. – Jimmyt1988 May 09 '15 at 22:40

4 Answers4

6

You might use a lambda:

FuncCall( []{ Foo f; f.Bar = "sausage"; f.Boo = 4; return f; }() );

Live example

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • Foo has no constructor parameters. – Jimmyt1988 May 09 '15 at 22:17
  • This is much nicer actually! i don't have to move that crap intot he function then... hmm hmmm yum yum yum. Lamdba in C++ seem to have saved the day here, I hate moving all these ownerships over even though they are declared just above it.. cool... lots of room for improvement but i'll mark u as the answer now. – Jimmyt1988 May 09 '15 at 22:26
  • I take it a smart compiler (read: gcc, clang, etc) would eliminate the lambda call and just inline it at compile time? – Cole Tobin May 09 '15 at 23:01
  • @ColeJohnson I'd hope so but I haven't checked/measured. There is no reason that the compiler can't do it, but I doubt anyone will give you a guarantee :) – Daniel Frey May 10 '15 at 06:13
2

Foo has to have a constructor or be an aggregate type:

class Foo {
  std::string bar;
  int boo;
public:
  Foo(std::string s, int i) : bar(std::move(s)), boo(i) {}
};

void FuncCall(std::unique_ptr<Foo> ptr) {}

int main() {
  FuncCall(make_unique<Foo>("sausage", 4));
}

struct Foo {
  std::string bar;
  int boo;
};

void FuncCall(std::unique_ptr<Foo> ptr) {}

int main() {
  std::unique_ptr<Foo> foo(new Foo{"sausage", 4});
  FuncCall(foo);
}

Or you can avoid pointers:

void FuncCall(Foo foo) {}

int main() {
  FuncCall({sausage", 4});
}
bames53
  • 86,085
  • 15
  • 179
  • 244
  • It's not that I personally do it any differently than you have here, but I just wanted to comment to wonder out loud whether you may inadvertently be promoting myths about `struct` in C++...? – Lightness Races in Orbit May 09 '15 at 22:18
  • This is helpful! Thank you very much. I'm guessing i'll have to stick with defining a massive massive massive list of paramters for all the paramters i want initialising... oh the joy. – Jimmyt1988 May 09 '15 at 22:22
  • 1
    @LightnessRacesinOrbit Perhaps. So to be clear for readers: `struct` and `class` are exactly the same except for the default accessibility of members and bases: `struct`s are public by default. I choose between the two simply on the basis of minimizing the number of explicit accessibility specifiers. Some coding standards mandate which should be used in specific use cases, such as 'use `struct` for aggregate types, otherwise use `class`.' – bames53 May 09 '15 at 23:28
  • @Jimmyt1988: Why is your class "massive massive massive"? – Lightness Races in Orbit May 09 '15 at 23:33
  • @Jimmyt1988 In C++ normally users shouldn't have to know very much about initializing an object: the class should take care of it, and allow the user to pass at most a few parameters to configure initialization. If the class is just a blob of data that the client code should be control of setting, common in C, then use an aggregate type. – bames53 May 09 '15 at 23:37
0

Be aware that the following:

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
    /* ... */
public:
    Foo() {}    // or Foo() = default or elision
};

int main() {
    Foo f;
    f.m_bar = "wunderBar";
}

Expands out to be along the lines of the following:

Foo* fptr = stack_allocate<Foo*>(sizeof(Foo));
// from ctor
fptr->m_bar.string("Bar"); // construct m_bar
fptr->m_baz.int(3);
fptr->m_boo.float(4.2);
// your code:
fptr->m_bar.operator=("wunderBar");

For similar reasons, you might want to look at the IL instructions for your C# construct - you'll find it's performing equally redundant operations (and in more complex situations, possibly boxing/unboxing).

Your C++ approach will also fail you when you incorporate non-copyable or non-movable types which will force you to pass pointers and/or bend your design.

What it /seems/ you are trying to do is recreate Python's optional parameters:

# C++-alike
class Foo(object):
    def __init__(self, Bar, Baz, Boo):
        ...

# C#-alike:
class Foo(object):
    def __init__(self, Bar="Bar", Baz=13, Boo=4.2):
        ...

C++ doesn't provide a direct way of doing this, the closest mechanisms are default parameters and operator overloading:

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
public:
    Foo(std::string bar="Bar", int baz=3, int boo=6.1)
        : m_bar(bar), m_baz(baz), m_boo(boo)
        {}
    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=13, boo=6.1);
     * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2);
     * Foo* f = new Foo("hello", 1, 1.); => new Foo(bar="hello", baz=1, boo=1.);
     * Foo* f = new Foo(42.); => invalid, arguments must be in order.
     */    
};

or

class Foo {
    std::string m_bar = "Bar";
    int m_baz = 3;
    float m_boo = 4.2;
public:
    Foo() = default;

    // allow Foo("hello")
    Foo(const char* bar) : m_bar(bar) {}
    Foo(const std::string& bar) : m_bar(bar) {}
    Foo(std::string&& bar) : m_bar(std::forward(bar)) {}

    // allow Foo(10, 12.)
    explicit Foo(int baz, float boo) : m_baz(baz), m_boo(boo) {}

    /* Foo* f = new Foo(); => new Foo(bar="Bar", baz=3, boo=4.2);
     * Foo* f = new Foo("hello"); => new Foo(bar="hello", baz=3, boo=4.2);
     * Foo* f = new Foo(1, 1.); => new Foo(bar="Bar", baz=1, boo=1.);
     * Foo* f = new Foo(42.); => invalid, no match
     */    
};

See http://ideone.com/yFIqlA for SSCE.

If you genuinely have a dozen different constructor configurations, you should probably rethink your design.

--- Edit ---

Note: It's not compulsory that you expose all parameters in the constructor:

class Foo {
    std::string m_user_supplied;
    std::time_t m_time;
public:
    Foo() : m_user_supplied(), m_time(0) {}
    Foo(std::string src) : m_user_supplied(src), m_time(0) {}
    void addTime(time_t inc) { m_time += inc; }
};

--- Edit 2 ---

"should maybe rethink your design" ... One problem with large, optional lists of parameters is growth. You are likely to wind up with parameters that depend on each other, contradict each other or interact with each other. You can either choose not to validate these or you can end up with complicated constructors.

struct Foo {
    ...
    FILE* m_output;
    const char* m_mode;
    ...

    Foo(..., FILE* output, const char* mode, ...)
    {
        ...
        if (output != nullptr) {
            ASSERT( output == nullptr || mode != nullptr );
            ... other requirements
        } else {
            if (mode != nullptr)
                ... might not be an error but it might be a bug ...
        }
        ...
    }
};

One approach to avoiding this is to use encapsulation/aggregation of related members.

class Foo {
    ...
    struct FileAccess {
        FILE* m_file;
        const char* m_mode;
        constexpr FileAccess() : m_file(nullptr), m_mode(nullptr) noexcept {}
        FileAccess(FILE* file, const char* mode) : m_file(file), m_mode(mode) {
            if (file == nullptr || mode == nullptr)
                throw invalid_argument("file/mode cannot be null");
        }
    };
    ...

    FileAccess m_access;

    Foo(..., FileAccess access, ...);
};

This can go a fair way to reducing the bloat. If your API is stable, you can use it with initializer lists (if your API is not stable and you do change will bite you in the ass)

auto fooWithFile = make_unique<Foo>{..., /*access=*/{stdout, "w"}, ...};
auto fooWithout  = make_unique<Foo>{..., /*access=*/{}, ...};

If you subsequently decide to stop using ctors and switch to using setters, this will translate reasonably well, since you can have overloaded "set" which takes one of the various configuration structs:

auto foo = make_unique<Foo>();
foo->set(FileAccess(stdout, "w"))
   ->set(Position(Right, -100, Top, 180))
   ->set(DimensionPercentage(80, 75));

vs

auto foo = make_unique<Foo>() { # pseudo based on if C++ had the C# syntax
    m_file = stdout;
    m_mode = "w";
    m_xPosition = -100;
    m_xPositionRel = Right;
    m_yPosition = -180;
    m_yPositionRel = Top;
    m_dimensionType = Percentage;
    m_xDimension = 80;
    m_yDimension = 75;
};
kfsone
  • 23,617
  • 2
  • 42
  • 74
  • "rethink your design", I'm not sure that's necessary... especially when all of these paramters are linked to the construction of the Foo ( or in my real class, It's a Window)... I have height, width, title, classname, handletowindow... but i guess it's good to share your opinion. – Jimmyt1988 May 09 '15 at 23:22
  • "should probably". Also I mean't "rethink" literally, not the same as "start over". IME a "rethink" can arrive at a similar design with small differences. I've worked with several projects that embraced the "big struct, fill out what you need" approach, and it has a lot of cons as it grows, such as conflicting settings and/or interdependent values ('baz' means nothing unless you set 'boo'; if 'parent' is set, you must specify a 'modality'). At that point, it may be better to make the default ctor trivial and use setters for everything. Or use aggregate parameters. – kfsone May 10 '15 at 00:08
  • Damn, i didn't even see the comment then about pseudo and you got me excited with the awesome syntax... lol... dammit. – Jimmyt1988 May 10 '15 at 00:34
-3

You can use uniform initialization to initialize it.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Also, doesn't answer the question. He's not asking about using {}s he's asking about optional arguments to a ctor. – kfsone May 09 '15 at 22:47