'Bracket initializing'. (C++)
Asked Answered
B

5

5

I'm learning C++ at the moment, C++ Primer plus. But I just felt like checking out the cplusplus website and skip a little forward to file handling.

I pretty much know the basics of file handling coming from java, php, visual basic. But I came across a pretty weird line.

ostream os(&fb);

fb represents a filebuf. I just don't get the syntax of this, but I can figure out that it's the same as:

ostream os = &fb;

But I never really read about this way of initializing variables.

So I'm wondering. Am I just senseless and missing out a real useful feature the entire time? Is this way of initializing just old? Is it something different?

Thanks in advance.

Barrier answered 26/12, 2009 at 15:1 Comment(0)
M
4

Perhaps you should read this and this

Mancy answered 26/12, 2009 at 15:5 Comment(3)
You should provide the answer here, in this post. The link might be down when someone else needs this question answered, and hey, if you want the rep, you should post the actual answer. It's common SO courtesy.Formative
Lightly paraphrased from the second link: ostream os = &fb; is copy initialization, and os is always initialized using ostream's copy ctor. (The "=" is just a syntax holdover from C; operator= is never called.) The compiler is actually allowed (but not required) to optimize away the copy construction in this kind of situation. If it does optimize it, the copy ctor must still be accessible. Guideline: Prefer using the form ostream os(&fb). It always works wherever ostream os = &fb works, and has other advantages (for instance, it can take multiple parameters).Radiant
In this case, of course, ostream os = &fb; will fail, since the stream can't be copied. Wondering why the question doesn't mention that.Paphian
F
6

Both forms perform initialization. The first syntax (with ()) is called direct-initialization syntax. The second syntax (with =) is called copy-initialization syntax. They will act same in most real-life cases, but there are indeed differences between the two.

In situations when types on the left-hand side (LHS) and right-hand side (RHS) are identical (ignoring any const/volatile qualifiers), both are indeed exactly the same. The language standard explicitly states that in this case the = form is equivalent to () form.

But when the types are different (and the LHS type is a class type), these two forms will generally work differently.

  • The copy-initialization form works as follows: convert the RHS value to the temporary object of LHS type (by any means possible: standard conversion, conversion operator, conversion constructor). And then use the copy constructor of the LHS class to copy the temporary object to the LHS object.

  • The direct initialization form work as follows: just consider all constructors of LHS and choose the most appropriate one by using overload resolution.

You can immediately notice that the copy-initialization syntax unconditionally uses the copy constructor (the copying and the intermediate temporary can be optimized away, but conceptually they are there). If the LHS class has no accessible copy-constructor, the copy-initialization unconditionally becomes ill-formed, while direct-initialization might still work.

Also, the keyword explicit applied to certain constructor will affect which form of initialization is available for which combinations of types.

Footloose answered 26/12, 2009 at 19:23 Comment(1)
Just going to pick on that exactly word (since the answer sounds like it's going for a really standard's answer): If the copy constructor is explicit, then the = syntax will fail in the case LHR and RHS are the same type, while the () form will not fail.Paphian
M
5

A small program to see when copy constructor is called and when overloaded assignment operator function is called:

#include <iostream>

using namespace std;

class test
{
    public:
        // default constructor.
        test()
        {
            cout<<"Default Ctor called"<<endl;
        }

        // copy constructor.
        test(const test& other)
        {
            cout<<"Copy Ctor called"<<endl;
        }

        // overloaded assignment operator function.
        test& operator=(const test& other)
        {
            cout<<"Overload operator function called"<<endl;
            return *this;
        }
};

int main(void) 
{
    test obj1;  // default constructor called.

    test obj2 = obj1; // copy constructor called.

    test obj3(obj2); // again copy constructor called.

    obj1 = obj2; // overloaded assignment operator function.

    return 0;
}

Output:

Default Ctor called
Copy Ctor called
Copy Ctor called
Overload operator function called

So in your case, the copy constructor of ostream is called in both the occasions.

Myeshamyhre answered 26/12, 2009 at 15:15 Comment(1)
Seems I was pretty correct then :D. Thanks for the clear explanation. Happy holidays!Barrier
M
4

Perhaps you should read this and this

Mancy answered 26/12, 2009 at 15:5 Comment(3)
You should provide the answer here, in this post. The link might be down when someone else needs this question answered, and hey, if you want the rep, you should post the actual answer. It's common SO courtesy.Formative
Lightly paraphrased from the second link: ostream os = &fb; is copy initialization, and os is always initialized using ostream's copy ctor. (The "=" is just a syntax holdover from C; operator= is never called.) The compiler is actually allowed (but not required) to optimize away the copy construction in this kind of situation. If it does optimize it, the copy ctor must still be accessible. Guideline: Prefer using the form ostream os(&fb). It always works wherever ostream os = &fb works, and has other advantages (for instance, it can take multiple parameters).Radiant
In this case, of course, ostream os = &fb; will fail, since the stream can't be copied. Wondering why the question doesn't mention that.Paphian
W
1

One important benefit of function-call initialization is that they also work with constructors that take multiple arguments. For example, an fstream constructor can take two parameters:

std::fstream file("filename", ios_base::out);

Until C++0x uniform initialization is widely available, function-call initialization is the only way to handle multiple argument constructors.

Wonted answered 27/12, 2009 at 4:22 Comment(0)
M
0

From my understanding, &var is a alias for the var variable and doesn't matter which one you use.

--------Addition -----------------

The below code is taken from Stroustrup book.From this its clear that both are alias to the same variable. It also says as below.

"The semantics of argument passing are defined to be those of initialization, so when called, increment's argument aa became another name of x." Thats why I refered to &x to be alias of x.

void increment(int& aa) { aa++; }

void f() { int x = 1; increment(x); }

Mistassini answered 27/12, 2009 at 4:6 Comment(1)
No, &var is the address of var. They have different types; if var is type T, then &var is type T*.Wonted

© 2022 - 2024 — McMap. All rights reserved.