user674155
user674155

Reputation:

Creating an object by function call in C++

I would like to create an object, that I'm only going to use only once, by a function call.

The two approaches I've tried give the same result, but as I'm new to C++, I'm not sure if they're appropriate.

#include <iostream>
using namespace std;

struct Foo {
    Foo(const int x, const int y): x(x), y(y) {};
    Foo(Foo & myfoo): x(myfoo.x), y(myfoo.y) {
        cout << "copying" << endl;
    };
    const int x, y;
};

int sum(const Foo & myfoo) {
    return myfoo.x + myfoo.y;
}

Foo make_foo(const int x, const int y) {
    Foo myfoo (x, y);
    return myfoo;
}

int main () {
    // version 1
    cout << 1 + sum(Foo(2,3)) << endl;
    // version 2
    cout << 1 + sum(make_foo(2,3)) << endl;
}

Which of these approaches is "more correct"? What's the difference? I ran the code above with gcc and clang and both times I get

6
6

which means the copy constructor wasn't called either time.

Related: Calling constructors in c++ without new

Edit: Thanks, I already know RVO. I was just wondering if one method is preferred over the other

Upvotes: 3

Views: 494

Answers (4)

user6557281
user6557281

Reputation: 1

In the first case cout << 1 + sum(Foo(2,3)) << endl; copy constructor will not be called since sum accepts the Foo reference. For later case your compiler is doing RVO hence copy constructor is not invoked. For more details read through Return Value Optimization and Copy Elision. Also this question has more details.

Upvotes: 0

Sopel
Sopel

Reputation: 1219

It is worth noting that functions that are usually named with make_* are useful for types that require template parameters (and they could be deduced during construction).

In C++ class template parameters are not deduced when calling the constructor (this will change in C++17) so you have to type them manually. By making a function that creates an object you can have it automatically deduced for you.

For example look up std::tuple, or std::pair

edit

I found a very related thread Deduction of the function

Upvotes: 0

Brent Bradburn
Brent Bradburn

Reputation: 54989

The two approaches are equivalent in this case.

What you are observing is Copy elision, or more specifically Return Value Optimization.

The compiler is smart enough to recognize that you don't need to construct the intermediate instance since you are just going to copy, then destroy it. Therefore, it just creates the final instance which is the target of the function return.

This optimization is explicitly allowed by the language standard, even though it means that your copy constructor gets bypassed. In other words, the functionality of a copy constructor getting called is not guaranteed.

Upvotes: 2

alter_igel
alter_igel

Reputation: 7212

The copy constructor isn't called because in sum(const Foo & myfoo), myfoo is being passed by reference and so doesn't need to be copied. IMHO I would say that a generally applicable constructor for Foo suffices for creating an instance. If you have specialized functionality that would clutter your Foo class with constructors, it might be more elegant to have a function elsewhere, like Foo loadFooFromFile(string filename)

Upvotes: 0

Related Questions