Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 3484738
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 18, 20262026-05-18T10:49:24+00:00 2026-05-18T10:49:24+00:00

I’m wondering the best form for my constructors. Here is some sample code: class

  • 0

I’m wondering the best form for my constructors. Here is some sample code:

class Y { ... }

class X
{
public:
  X(const Y& y) : m_y(y) {} // (a)
  X(Y y) : m_y(y) {} // (b)
  X(Y&& y) : m_y(std::forward<Y>(y)) {} // (c)

  Y m_y;
}

Y f() { return ... }

int main()
{
  Y y = f();
  X x1(y); // (1)
  X x2(f()); // (2)
}

From what I understand, this is the best the compiler can do in each situation.

(1a) y is copied into x1.m_y (1 copy)

(1b) y is copied into the argument of the constructor of X, and then copied into x1.m_y (2 copies)

(1c) y is moved into x1.m_y (1 move)

(2a) result of f() is copied into x2.m_y (1 copy)

(2b) f() is constructed into the argument of the constructor, and then copied to x2.m_y (1 copy)

(2c) f() is created on the stack, and then moved into x2.m_y (1 move)

Now a few questions:

  1. On both counts, pass by const reference is no worse, and sometimes better than pass by value. This seems to go against the discussion on “Want Speed? Pass by Value.”. For C++ (not C++0x), should I stick with pass by const reference for these constructors, or should I go pass by value? And for C++0x, should I do pass by rvalue reference over pass by value?

  2. For (2), I’d prefer if the temporary was constructed directly into x.m_y. Even the rvalue version I think requires a move which, unless the object allocates dynamic memory, is as much work as a copy. Is there any way to code this so the compiler is permitted to avoid these copies and moves?

  3. I’ve made a lot of assumptions in both what I think the compiler can do best and in my questions themselves. Please correct any of these if they are incorrect.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-18T10:49:25+00:00Added an answer on May 18, 2026 at 10:49 am

    I’ve thrown together some examples. I used GCC 4.4.4 in all of this.

    Simple case, without -std=c++0x

    First, I put together a very simple example with two classes that accept an std::string each.

    #include <string>
    #include <iostream>
    
    struct A /* construct by reference */
      {
        std::string s_;
    
        A (std::string const &s) : s_ (s)
          {
            std::cout << "A::<constructor>" << std::endl;
          }
        A (A const &a) : s_ (a.s_)
          {
            std::cout << "A::<copy constructor>" << std::endl;
          }
        ~A ()
          {
            std::cout << "A::<destructor>" << std::endl;
          }
      };
    
    struct B /* construct by value */
      {
        std::string s_;
    
        B (std::string s) : s_ (s)
          {
            std::cout << "B::<constructor>" << std::endl;
          }
        B (B const &b) : s_ (b.s_)
          {
            std::cout << "B::<copy constructor>" << std::endl;
          }
        ~B ()
          {
            std::cout << "B::<destructor>" << std::endl;
          }
      };
    
    static A f () { return A ("string"); }
    static A f2 () { A a ("string"); a.s_ = "abc"; return a; }
    static B g () { return B ("string"); }
    static B g2 () { B b ("string"); b.s_ = "abc"; return b; }
    
    int main ()
      {
        A a (f ());
        A a2 (f2 ());
        B b (g ());
        B b2 (g2 ());
        
        return 0;
      }
    

    The output of that program on stdout is as follows:

    A::<constructor>
    A::<constructor>
    B::<constructor>
    B::<constructor>
    B::<destructor>
    B::<destructor>
    A::<destructor>
    A::<destructor>
    

    Conclusion

    GCC was able to optimize each and every temporary A or B away.
    This is consistent with the C++ FAQ. Basically, GCC may (and is willing to) generate code that constructs a, a2, b, b2 in place, even if a function is called that appearantly returns by value. Thereby GCC can avoid many of the temporaries whose existence one might have "inferred" by looking at the code.

    The next thing we want to see is how often std::string is actually copied in the above example. Let’s replace std::string with something we can observe better and see.

    Realistic case, without -std=c++0x

    #include <string>
    #include <iostream>
    
    struct S
      {
        std::string s_;
    
        S (std::string const &s) : s_ (s)
          {
            std::cout << "  S::<constructor>" << std::endl;
          }
        S (S const &s) : s_ (s.s_)
          {
            std::cout << "  S::<copy constructor>" << std::endl;
          }
        ~S ()
          {
            std::cout << "  S::<destructor>" << std::endl;
          }
      };
    
    struct A /* construct by reference */
      {
        S s_;
    
        A (S const &s) : s_ (s) /* expecting one copy here */
          {
            std::cout << "A::<constructor>" << std::endl;
          }
        A (A const &a) : s_ (a.s_)
          {
            std::cout << "A::<copy constructor>" << std::endl;
          }
        ~A ()
          {
            std::cout << "A::<destructor>" << std::endl;
          }
      };
    
    struct B /* construct by value */
      {
        S s_;
    
        B (S s) : s_ (s) /* expecting two copies here */
          {
            std::cout << "B::<constructor>" << std::endl;
          }
        B (B const &b) : s_ (b.s_)
          {
            std::cout << "B::<copy constructor>" << std::endl;
          }
        ~B ()
          {
            std::cout << "B::<destructor>" << std::endl;
          }
      };
    
    /* expecting a total of one copy of S here */
    static A f () { S s ("string"); return A (s); }
    
    /* expecting a total of one copy of S here */
    static A f2 () { S s ("string"); s.s_ = "abc"; A a (s); a.s_.s_ = "a"; return a; }
    
    /* expecting a total of two copies of S here */
    static B g () { S s ("string"); return B (s); }
    
    /* expecting a total of two copies of S here */
    static B g2 () { S s ("string"); s.s_ = "abc"; B b (s); b.s_.s_ = "b"; return b; }
    
    int main ()
      {
        A a (f ());
        std::cout << "" << std::endl;
        A a2 (f2 ());
        std::cout << "" << std::endl;
        B b (g ());
        std::cout << "" << std::endl;
        B b2 (g2 ());
        std::cout << "" << std::endl;
        
        return 0;
      }
    

    And the output, unfortunately, meets the expectation:

      S::<constructor>
      S::<copy constructor>
    A::<constructor>
      S::<destructor>
    
      S::<constructor>
      S::<copy constructor>
    A::<constructor>
      S::<destructor>
    
      S::<constructor>
      S::<copy constructor>
      S::<copy constructor>
    B::<constructor>
      S::<destructor>
      S::<destructor>
    
      S::<constructor>
      S::<copy constructor>
      S::<copy constructor>
    B::<constructor>
      S::<destructor>
      S::<destructor>
    
    B::<destructor>
      S::<destructor>
    B::<destructor>
      S::<destructor>
    A::<destructor>
      S::<destructor>
    A::<destructor>
      S::<destructor>
    

    Conclusion
    GCC was not able to optimize away the temporary S created by B‘s constructor. Using the default copy constructor of S did not change that. Changing f, g to be

    static A f () { return A (S ("string")); } // still one copy
    static B g () { return B (S ("string")); } // reduced to one copy!
    

    did have the indicated effect. It appears that GCC is willing to construct the argument to B‘s constructor in place but hesitant to construct B‘s member in place.
    Do note that still no temporary A or B are created. That means a, a2, b, b2 are still being constructed in place. Cool.

    Let’s now investigate how the new move semantics may influence the second example.

    Realistic case, with -std=c++0x
    Consider adding the following constructor to S,

        S (S &&s) : s_ ()
          {
            std::swap (s_, s.s_);
            std::cout << "  S::<move constructor>" << std::endl;
          }
    

    changing B‘s constructor to

        B (S &&s) : s_ (std::move (s)) /* how many copies?? */
          {
            std::cout << "B::<constructor>" << std::endl;
          }
    

    and changing g() and g2() to

    static B g () { S s ("string"); return B (std::move(s)); }
    
    static B g2 () { S s ("string"); s.s_ = "abc"; B b (std::move(s)); b.s_.s_ = "b"; return b; }
    

    We get this output

      S::<constructor>
      S::<copy constructor>
    A::<constructor>
      S::<destructor>
    
      S::<constructor>
      S::<copy constructor>
    A::<constructor>
      S::<destructor>
    
      S::<constructor>
      S::<move constructor>
    B::<constructor>
      S::<destructor>
    
      S::<constructor>
      S::<move constructor>
    B::<constructor>
      S::<destructor>
    
    B::<destructor>
      S::<destructor>
    B::<destructor>
      S::<destructor>
    A::<destructor>
      S::<destructor>
    A::<destructor>
      S::<destructor>
    

    So, we were able to replace four copies with two moves by using pass by rvalue.

    But we actually constructed a broken program.

    Recall the updated g() and g2(),

    static B g ()  { S s ("string"); return B (std::move(s)); /* s is zombie now */ }
    static B g2 () { S s ("string"); s.s_ = "abc"; B b (std::move(s)); /* s is zombie now */ b.s_.s_ = "b"; return b; }
    

    The marked location shows the problem. A move was done on an object that is not a temporary. s will be in a valid but unspecified state once it is moved from. Because rvalue references behave like lvalue references except that they bind to rvalues, we had to cast s to an rvalue, in order to be passed to B‘s constructor that now accepts an rvalue reference. So we must not forget to overload B‘s constructor with one that takes a constant lvalue reference.

        B (S const &s) : s_ (s)
          {
            std::cout << "B::<constructor2>" << std::endl;
          }
    

    You will then notice that both g, g2 cause "constructor2" to be called, since the symbol s in either case is a better fit for a const reference than for an rvalue reference.
    We can persuade the compiler to do a move in g in either of two ways:

    static B g ()  { return B (S ("string")); }
    static B g ()  { S s ("string"); return B (std::move (s)); }
    

    Conclusions

    Do return-by-value. The code will be more readable than "fill a reference I give you" code and faster and maybe even more exception safe.
    Consider changing f to

    static void f (A &result) { A tmp; /* ... */ result = tmp; } /* or */
    static void f (A &result) { /* ... */ result = A (S ("string")); }
    

    That will meet the strong guarantee only if A‘s assignment provides it. The copy into result cannot be skipped, neither can tmp be constructed in place of result, since result is not being constructed. Thus, it is slower than before, where no copying was necessary. C++0x compilers and move assignment operators would reduce the overhead, but it’s still slower than to return-by-value.

    Return-by-value provides the strong guarantee more easily. The object is constructed in place. If one part of that fails and other parts have already been constructed, normal unwinding will clean up and, as long as S‘s constructor fulfills the basic guarantee with regard to its own members and the strong guarantee with regard to global items, the whole return-by-value process actually provides the strong guarantee.

    Always pass by value if you’re going to copy (onto the stack) anyway
    As discussed in Want speed? Pass by value.. The compiler may generate code that constructs, if possible, the caller’s argument in place, eliminating the copy, which it cannot do when you take by reference and then copy manually. Principal example:
    Do NOT write this (taken from cited article)

    T& T::operator=(T const& x) // x is a reference to the source
    { 
        T tmp(x);          // copy construction of tmp does the hard work
        swap(*this, tmp);  // trade our resources for tmp's
        return *this;      // our (old) resources get destroyed with tmp 
    }
    

    but always prefer this

    T& T::operator=(T x)    // x is a copy of the source; hard work already done
    {
        swap(*this, x);  // trade our resources for x's
        return *this;    // our (old) resources get destroyed with x
    }
    

    If you want to copy to a non-stack frame location pass by const reference pre C++0x and additionally pass by rvalue reference post C++0x
    We already saw this. Pass by reference causes less copies to take place when in place construction is impossible than pass by value. And C++0x’s move semantics may replace many copies with fewer and cheaper moves. But do keep in mind that moving will make a zombie out of the object that has been moved from. Moving is not copying. Just providing a constructor that accepts rvalue references may break things, as shown above.

    If you want to copy to a non-stack frame location and have swap, consider passing by value anyway (pre C++0x)
    If you have cheap default construction, that combined with a swap may be more efficient than copying stuff around. Consider S‘s constructor to be

        S (std::string s) : s_ (/* is this cheap for your std::string? */)
          {
            s_.swap (s); /* then this may be faster than copying */
            std::cout << "  S::<constructor>" << std::endl;
          }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

link Im having trouble converting the html entites into html characters, (&# 8217;) i
I'm parsing an RSS feed that has an &#8217; in it. SimpleXML turns this
I'm trying to decode HTML entries from here NYTimes.com and I cannot figure out
I have just tried to save a simple *.rtf file with some websites and
I ran into a problem. Wrote the following code snippet: teksti = teksti.Trim() teksti
I have some data like this: 1 2 3 4 5 9 2 6
I have this code: - (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock { NSString *someString = [[NSString
I have text I am displaying in SIlverlight that is coming from a CMS
Seemingly simple, but I cannot find anything relevant on the web. What is the
Does anyone know how can I replace this 2 symbol below from the string

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.