About FAQ 10.18

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • James Aguilar

    About FAQ 10.18

    Hey all, I have a quick question with FAQ 10.18. I was running into a
    problem similar to this yesterday, fortunately, Victor Bazarov helped me out
    there. I do have a question about the way the compiler interprets this
    line, the workaround for the problem of using a temporary in a constructor:

    Foo x = Foo(Bar());

    My question is, what exactly does this do? Do is create a default
    constructed Foo and then overwrite it using the assignment operator with
    whatever is on the right side? Or does it simply initialize a Foo object
    with whatever is on the right side? Can I count on this to happen the same
    way everywhere? Lastly, is there any way that this pattern of using
    assignment can hurt me silently (i.e. performance-wise or any other way)?

    Sorry if the question seems obscure or useless. It's just that I plan on
    doing this for a long time, and I'd like to know now how I need to proceed
    so I don't shoot myself in the foot later. =)

    - JFA


  • James Aguilar

    #2
    Re: About FAQ 10.18

    A quick note: I tried looking at the assembly code of a short test program I
    wrote. Here is what came out:

    The program is:

    int main()
    {
    int x(10);
    int y = 20;
    return 0;
    }

    The assembly is:

    _main:
    pushl %ebp
    movl %esp, %ebp
    subl $24, %esp
    andl $-16, %esp
    movl $0, %eax
    movl %eax, -12(%ebp)
    movl -12(%ebp), %eax
    call __alloca
    call ___main
    movl $10, -4(%ebp) /*<--Does x*/
    movl $20, -8(%ebp) /*<--Does y*/
    movl $0, %eax
    leave
    ret

    So on my compiler and my platform for primitives, my earlier question is
    answered in the affirmative (i.e. yes, using an = sign to assign an object
    that is currently being initialized is OK and has no effect). Now, my real
    question is "Is this the case everywhere?" Thanks!


    Comment

    • Dietmar Kuehl

      #3
      Re: About FAQ 10.18

      James Aguilar wrote:[color=blue]
      > Foo x = Foo(Bar());
      >
      > My question is, what exactly does this do? Do is create a default
      > constructed Foo and then overwrite it using the assignment operator[/color]
      with[color=blue]
      > whatever is on the right side?[/color]

      No.
      [color=blue]
      > Or does it simply initialize a Foo object
      > with whatever is on the right side?[/color]

      Not exactly. The above conceptually does the following in that
      sequence:

      - default construct a 'Bar' temporary
      - construct a 'Foo' temporary with the 'Bar' temporary as parameter
      - copy construct the 'Foo' variable 'x' from the 'Foo' temporary

      However, the compiler is permitted to elide the 'Foo' temporary and
      immediately construct the 'Foo' variable 'x'. Even if the compiler does
      so,
      it is required to check that 'Foo's copy constructor is accessible.
      [color=blue]
      > Can I count on this to happen the same way everywhere?[/color]

      No.
      [color=blue]
      > Lastly, is there any way that this pattern of using
      > assignment can hurt me silently (i.e. performance-wise or any other[/color]
      way)?

      If 'Foo's copy constructor is relatively expensive, it may hurt you on
      platforms or with compiler sittings where the temporary is not elided.
      [color=blue]
      > Sorry if the question seems obscure or useless. It's just that I[/color]
      plan on[color=blue]
      > doing this for a long time, and I'd like to know now how I need to[/color]
      proceed[color=blue]
      > so I don't shoot myself in the foot later. =)[/color]

      A safer approach is to use the extra parenthesis:

      Foo x((Bar()));

      However, it is not only safer but also visually more obscure. Since
      more is
      better, you might want to go for more obscurity in this case :-)
      --
      <mailto:dietmar _kuehl@yahoo.co m> <http://www.dietmar-kuehl.de/>
      <http://www.contendix.c om> - Software Development & Consulting

      Comment

      • Howard

        #4
        Re: About FAQ 10.18


        "Dietmar Kuehl" <dietmar_kuehl@ yahoo.com> wrote in message > A safer
        approach is to use the extra parenthesis:[color=blue]
        >
        > Foo x((Bar()));
        >
        > However, it is not only safer but also visually more obscure. Since
        > more is
        > better, you might want to go for more obscurity in this case :-)[/color]

        Personally, I prefer to be less obscure, and would do this instead:

        Bar b;
        Foo x(b);

        This is clearer as to exactly what's going on. It also lets me examine the
        Bar object (b) in the debugger easily. I dislike unnamed temporaries, and
        the issue under discussion is just another reason why.
        Just my 2 cents worth...
        -H


        Comment

        • Derek

          #5
          Re: About FAQ 10.18

          Dietmar Kuehl wrote:[color=blue]
          > Not exactly. The above conceptually does the following in that
          > sequence:
          >
          > - default construct a 'Bar' temporary
          > - construct a 'Foo' temporary with the 'Bar' temporary as parameter
          > - copy construct the 'Foo' variable 'x' from the 'Foo' temporary[/color]
          ....[color=blue][color=green]
          > > Can I count on this to happen the same way everywhere?[/color]
          >
          > No.[/color]

          Just so I understand, are you saying that if I write

          Foo x = Foo(1,2,3);

          the compiler may choose to elide the temporary and generate the
          same code as if I had written

          Foo x(1,2,3);

          but that it is not required to?

          Comment

          • Dietmar Kuehl

            #6
            Re: About FAQ 10.18

            Derek wrote:[color=blue]
            > Just so I understand, are you saying that if I write
            >
            > Foo x = Foo(1,2,3);
            >
            > the compiler may choose to elide the temporary and generate the
            > same code as if I had written
            >
            > Foo x(1,2,3);
            >
            > but that it is not required to?[/color]

            Right. The reason behind this is pretty simple: the semantic of
            the initialization syntax is defined in terms of a general expression
            like this:

            T obj = expr;

            The special case where 'expr' is actually a constructor call for a
            'T' object is not specifically addressed. Thus, the behavior is as
            if the expression 'expr' is executed first, followed by the
            initialization. On the other hand, temporaries can always be elided
            but there is no guarantee that they actually are elided.
            --
            <mailto:dietmar _kuehl@yahoo.co m> <http://www.dietmar-kuehl.de/>
            <http://www.contendix.c om> - Software Development & Consulting

            Comment

            • Andrey Tarasevich

              #7
              Re: About FAQ 10.18

              Derek wrote:[color=blue]
              > ...
              > Just so I understand, are you saying that if I write
              >
              > Foo x = Foo(1,2,3);
              >
              > the compiler may choose to elide the temporary and generate the
              > same code as if I had written
              >
              > Foo x(1,2,3);
              >
              > but that it is not required to?[/color]

              Yes, that's exactly correct.

              --
              Best regards,
              Andrey Tarasevich

              Comment

              • Micah Cowan

                #8
                Re: About FAQ 10.18

                Dietmar Kuehl wrote:
                [color=blue]
                > Derek wrote:
                >[color=green]
                >>Just so I understand, are you saying that if I write
                >>
                >> Foo x = Foo(1,2,3);
                >>
                >>the compiler may choose to elide the temporary and generate the
                >>same code as if I had written
                >>
                >> Foo x(1,2,3);
                >>
                >>but that it is not required to?[/color]
                >
                >
                > Right. The reason behind this is pretty simple: the semantic of
                > the initialization syntax is defined in terms of a general expression
                > like this:
                >
                > T obj = expr;
                >
                > The special case where 'expr' is actually a constructor call for a
                > 'T' object is not specifically addressed. Thus, the behavior is as
                > if the expression 'expr' is executed first, followed by the
                > initialization. On the other hand, temporaries can always be elided
                > but there is no guarantee that they actually are elided.[/color]

                It seems to me, though, that if the result of constructing a
                temporary of type Foo with int args and initializing a new Foo
                with that object is determinably different from initializing the
                new Foo directly with those args, then both constructions must
                occur. Example:

                -----

                #include <iostream>

                class Foo {
                bool _initialized_by _another_foo;
                int _one, _two, _three;

                public:
                Foo(const Foo &other)
                {
                _one = other._one;
                _two = other._two;
                _three = other._three;
                _initialized_by _another_foo = true;
                }

                Foo(int one, int two, int three)
                {
                _one = one;
                _two = two;
                _three = three;
                _initialized_by _another_foo = false;
                }

                bool was_copy_constr ucted() const
                {
                return _initialized_by _another_foo;
                }
                };

                std::ostream &operator << (std::ostream &s, const Foo &f)
                {
                s << (f.was_copy_con structed() ? "true" : "false");
                }

                int main(void)
                {
                using std::cout;
                using std::endl;

                Foo a(1,2,3);
                Foo b(a);
                Foo c = Foo(1,2,3);

                cout << a << endl << b << endl << c << endl;
                }

                -----

                I was expecting the above program to output

                false
                true
                true

                but with g++ 3.2 (apparently a prerelease version of it), I get:

                false
                true
                false

                Meaning that I can definitely determine that c was not
                constructed from another Foo.

                I get the same results with:

                Foo c(Foo(1,2,3));

                Can anyone explain this to me? From what I've been able to find
                in the standard, the program must behave as if the temp had been
                constructed, producing "true" for the last line.

                Comment

                • David Harmon

                  #9
                  Re: About FAQ 10.18

                  On Thu, 06 Jan 2005 12:28:02 -0800 in comp.lang.c++, Micah Cowan
                  <micah@cowan.na me> wrote,[color=blue]
                  >It seems to me, though, that if the result of constructing a
                  >temporary of type Foo with int args and initializing a new Foo
                  >with that object is determinably different from initializing the
                  >new Foo directly with those args, then both constructions must
                  >occur.[/color]

                  No, the rule is that the temporary may be eliminated. It is up to
                  you to make sure that your program works correctly either way.

                  Comment

                  • Andrey Tarasevich

                    #10
                    Re: About FAQ 10.18

                    Micah Cowan wrote:[color=blue]
                    > ...
                    > I was expecting the above program to output
                    >
                    > false
                    > true
                    > true
                    >
                    > but with g++ 3.2 (apparently a prerelease version of it), I get:
                    >
                    > false
                    > true
                    > false
                    >
                    > Meaning that I can definitely determine that c was not
                    > constructed from another Foo.
                    >
                    > I get the same results with:
                    >
                    > Foo c(Foo(1,2,3));
                    >
                    > Can anyone explain this to me? From what I've been able to find
                    > in the standard, the program must behave as if the temp had been
                    > constructed, producing "true" for the last line.[/color]

                    One important detail about temporary elimination is that the standard
                    allows compilers to eliminate the intermediate temporary object
                    _regardless_ of any additional [side-]effects of the copy constructor of
                    the class. Indeed, you can determine which approach the compiler has
                    chosen in each particular case (by using the above technique, for
                    example), but that still doesn't mean that the behavior must be
                    consistent across compilers and even within one given compiler. In other
                    words, a well-written C++ program shall not depend on this behavior in
                    any essential way. Otherwise, the program's behavior wouldn't be specified.

                    --
                    Best regards,
                    Andrey Tarasevich

                    Comment

                    Working...