Eliminated copy requires copy-constructor

Discussion in 'C++' started by kjm424, Dec 14, 2006.

  1. kjm424

    kjm424 Guest

    I'm wondering if someone can shed some light on a decision made in the
    C++ standard.

    It would seem that when passing a temporary into a function that takes
    a const reference, the copy constructor must be accessible, even though
    the copy can be eliminated.

    Could somebody tell me the reason for this? Section 8.5.3 of the final
    draft explains the requirement (I think), but with no explanation as to
    why.

    I have included some example source in case my explanation is lacking.

    Thanks for any help.

    #include <iostream>

    class A
    {
    public:

    A(int i) : m_val(i)
    { }

    int val() const
    {
    return m_val;
    }

    private:

    A(const A&);
    A& operator=(const A&);

    int m_val;
    };

    void output(const A& a)
    {
    std::cerr << "Value: " << a.val() << '\n';
    }

    int main()
    {
    A a(3);

    output(a); // Works

    output(A(5)); // Fails to compile - copy constructor not
    accesible

    return 0;
    }
     
    kjm424, Dec 14, 2006
    #1
    1. Advertising

  2. kjm424

    Gavin Deane Guest

    kjm424 wrote:
    > I'm wondering if someone can shed some light on a decision made in the
    > C++ standard.
    >
    > It would seem that when passing a temporary into a function that takes
    > a const reference, the copy constructor must be accessible, even though
    > the copy can be eliminated.
    >
    > Could somebody tell me the reason for this? Section 8.5.3 of the final
    > draft explains the requirement (I think), but with no explanation as to
    > why.


    Think about it logically. By making the copy constructor inaccessible
    you are saying that the concept of copying is not applicable to that
    type. As you have discovered, the way in which you are using references
    requires the concept of copying. For your class, the concept of copying
    is not applicable so it makes no sense for your class to be used there
    and the compiler quite rightly forbids it.

    There is the completely independent point that, *if* the concept of
    copying is applicable to a type, there are some situations in which, as
    an optimisation, the *act* of copying may be elided. If the language
    were to ignore whether the *concept* of copying was applicable in such
    cases, then whether your code even compiled or not would depend solely
    on the compiler's optimisation settings, which would be mad.

    Gavin Deane
     
    Gavin Deane, Dec 14, 2006
    #2
    1. Advertising

  3. kjm424

    kjm424 Guest

    Thanks for your response Gavin.

    > There is the completely independent point that, *if* the concept of
    > copying is applicable to a type, there are some situations in which, as
    > an optimisation, the *act* of copying may be elided. If the language
    > were to ignore whether the *concept* of copying was applicable in such
    > cases, then whether your code even compiled or not would depend solely
    > on the compiler's optimisation settings, which would be mad.


    This makes sense, optimisation settings shouldn't make any difference
    to whether code is deemed correct or not.

    > Think about it logically. By making the copy constructor inaccessible
    > you are saying that the concept of copying is not applicable to that
    > type. As you have discovered, the way in which you are using references
    > requires the concept of copying. For your class, the concept of copying
    > is not applicable so it makes no sense for your class to be used there
    > and the compiler quite rightly forbids it.


    I can understand if the type being passed in is different to the
    function argument, such as passing an int into a function taking a
    const double reference, but when they (the type getting passed in and
    the expected type) are both the same type, I fail to see why a copy is
    required.

    Don't get me wrong, I'm not saying that I think that the compiler or
    the standard is wrong, I'm just trying to understand why the way in
    which I am using references requires the concept of copying in the
    first place.
     
    kjm424, Dec 14, 2006
    #3
  4. kjm424

    Gavin Deane Guest

    kjm424 wrote:
    > I can understand if the type being passed in is different to the
    > function argument, such as passing an int into a function taking a
    > const double reference, but when they (the type getting passed in and
    > the expected type) are both the same type, I fail to see why a copy is
    > required.
    >
    > Don't get me wrong, I'm not saying that I think that the compiler or
    > the standard is wrong, I'm just trying to understand why the way in
    > which I am using references requires the concept of copying in the
    > first place.


    Ah, I see. I may have slightly missed the point of your question. So,
    are you asking why, in the following program (slightly adapted from
    yours), the code marked "Not allowed" requires any copying to
    initialise the references?

    class A
    {
    public:
    A(int i) : m_val(i) {}
    private:
    A(const A&);
    A& operator=(const A&);
    int m_val;
    };

    void foo(const A& a) {}

    int main()
    {
    // Allowed
    A a(3);
    foo(a);
    const A& r1 = a;

    // Not allowed
    foo(A(5));
    const A& r2 = A(5);
    }

    If that's your question, then I *think* the answer is in 8.5.3, but
    that section is at the limit of my reading standardese. I'm intrigued
    enough that I might take a deeper look when I get the time. I believe
    the applicable part is this

    <quote>
    If the initializer expression is an rvalue, with T2 a class type, and
    "cv1 T1" is reference compatible with "cv2 T2," the reference
    is bound in one of the following ways (the choice is
    implementationdefined):

    - The reference is bound to the object represented by the rvalue (see
    3.10) or to a subobject within that object.
    - A temporary of type "cv1 T2" [sic] is created, and a
    constructor is called to copy the entire rvalue object into the
    temporary. The reference is bound to the temporary or to a subobject
    within the temporary.
    </quote>

    which suggests to me that whether the concept of copying is actually
    applicable here is implementation defined, which somewhat undermines my
    original argument.

    But as I say, I'm not fully confident of my understanding of the
    standardese here.

    Gavin Deane
     
    Gavin Deane, Dec 14, 2006
    #4
  5. kjm424

    Greg Guest

    kjm424 wrote:
    > Thanks for your response Gavin.
    >
    > > There is the completely independent point that, *if* the concept of
    > > copying is applicable to a type, there are some situations in which, as
    > > an optimisation, the *act* of copying may be elided. If the language
    > > were to ignore whether the *concept* of copying was applicable in such
    > > cases, then whether your code even compiled or not would depend solely
    > > on the compiler's optimisation settings, which would be mad.

    >
    > This makes sense, optimisation settings shouldn't make any difference
    > to whether code is deemed correct or not.
    >
    > > Think about it logically. By making the copy constructor inaccessible
    > > you are saying that the concept of copying is not applicable to that
    > > type. As you have discovered, the way in which you are using references
    > > requires the concept of copying. For your class, the concept of copying
    > > is not applicable so it makes no sense for your class to be used there
    > > and the compiler quite rightly forbids it.

    >
    > I can understand if the type being passed in is different to the
    > function argument, such as passing an int into a function taking a
    > const double reference, but when they (the type getting passed in and
    > the expected type) are both the same type, I fail to see why a copy is
    > required.


    The copy is needed because the program is passing an rvalue, A(5), as
    an argument to a function, output(), that is expecting an object as the
    parameter (a const reference, in this case). So in order to provide the
    type of parameter that output() is expecting, the compiler must (at
    least conceptually) copy the rvalue into an lvalue A object and pass
    that object to the output() as a parameter. But with A's copy
    constructor inaccessible, the compiler is not allowed to copy the A(5)
    temporary so the program cannot be compiled.

    Greg
     
    Greg, Dec 14, 2006
    #5
  6. kjm424

    vijay Guest

    kjm424 wrote:
    > I'm wondering if someone can shed some light on a decision made in the
    > C++ standard.
    >
    > It would seem that when passing a temporary into a function that takes
    > a const reference, the copy constructor must be accessible, even though
    > the copy can be eliminated.
    >
    > Could somebody tell me the reason for this? Section 8.5.3 of the final
    > draft explains the requirement (I think), but with no explanation as to
    > why.
    >
    > I have included some example source in case my explanation is lacking.
    >
    > Thanks for any help.
    >
    > #include <iostream>
    >
    > class A
    > {
    > public:
    >
    > A(int i) : m_val(i)
    > { }
    >
    > int val() const
    > {
    > return m_val;
    > }
    >
    > private:
    >
    > A(const A&);
    > A& operator=(const A&);
    >
    > int m_val;
    > };
    >
    > void output(const A& a)
    > {
    > std::cerr << "Value: " << a.val() << '\n';
    > }
    >
    > int main()
    > {
    > A a(3);
    >
    > output(a); // Works
    >
    > output(A(5)); // Fails to compile - copy constructor not
    > accesible
    >
    > return 0;
    > }


    output(A(5));
    This works perfectly on MSVC++ 6.0 .
     
    vijay, Dec 14, 2006
    #6
  7. kjm424

    Gavin Deane Guest

    vijay wrote:
    > output(A(5));
    > This works perfectly on MSVC++ 6.0 .


    I don't think that's saying much. IIRC MSVC++ 6.0 allows this

    class A
    {
    public:
    A(int i) {}
    };

    // Note - non-const
    void foo(A& ref) {}

    int main()
    {
    foo(A(42));
    }

    so it wouldn't surprise me if it got other things wrong in this area
    too.

    Gavin Deane
     
    Gavin Deane, Dec 14, 2006
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    2
    Views:
    396
    Hendrik Maryns
    Mar 6, 2006
  2. Aire
    Replies:
    3
    Views:
    489
    Mike Wahler
    Jan 25, 2004
  3. Generic Usenet Account
    Replies:
    10
    Views:
    2,353
  4. cinsk
    Replies:
    35
    Views:
    2,733
    James Kanze
    Oct 11, 2010
  5. Claudiu Popa

    Checking against NULL will be eliminated?

    Claudiu Popa, Mar 2, 2011, in forum: Python
    Replies:
    15
    Views:
    1,971
    Carl Banks
    Mar 4, 2011
Loading...

Share This Page