home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.lang.c++.moderated      Moderated discussion of C++ superhackery      33,346 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 32,213 of 33,346   
   Scott Meyers to Dave Abrahams   
   Re: Internal move vs. copy in std::vecto   
   28 Apr 12 20:09:39   
   
   From: NeverRead@aristeia.com   
      
   On 4/27/2012 11:38 PM, Dave Abrahams wrote:   
   >> An implication is that move operations may replace copy operations   
   >> only if the move operations are known not to throw.   
   >   
   > That's true in the context of vector::push_back   
      
   I disagree.  That's not what I believe the Standard guarantees about   
   push_back, nor what all library implementations offer.   
      
   Continuing with a quote from me, followed by Dave's reply:   
   >> 23.3.6.5/1 specifies the details for std::vector's insert,   
   >> emplace_back, emplace, and push_back operations. It says:   
   >>   
   >>>    If an exception is thrown other than by the copy constructor,   
   >>>    move constructor, assignment operator, or move assignment   
   >>>    operator of T or by any InputIterator operation there are no   
   >>>    effects. If an exception is thrown by the move constructor of a   
   >>>    non-CopyInsertable T, the effects are unspecified.   
   >>   
   >> This provision overrules the specification in 23.2.1/10 (as   
   >> 23.2.1/10 expressly notes). My reading of it is that if the copy   
   >> constructor, the move constructor, the (copy) assignment operator,   
   >> or the move assignment operator of the contained type (T) throw,   
   >> all bets are off: there is no exception-safety guarantee offered by   
   >> the standard.   
   >   
   > Not quite; there's no strong guarantee, but the standard still gives   
   > the basic guarantee—as it does everywhere and all the time—if the   
   > copy constructor of T throws.   
      
   Where is this specified?  It's not in the text above.   
      
   > And I think saying "all bets are off" in this context is a bad idea;   
   > people usually associate that with undefined behavior.   
      
   Which is actually what I believe the above says: if the copy   
   constructor throws, the behavior is not specified. Is it specified   
   elsewhere?  I don't see how 23.2.1/10 can apply, because it expressly   
   refers to 23.3.6.5 as an example of a place where 23.2.1/10 is   
   overruled.   
      
   > But I can report confidently on the intention of the committee.   
      
   Which brings me back to the observed divergent behavior between gcc   
   4.7 and VC11 beta on shifting element values when push_back requires   
   an increase in vector capacity.  VC11 moves, even when both copy and   
   move operations may throw, while gcc 4.7 copies unless the move   
   operations are known to not throw. My sample code is below.  Assuming   
   it's a valid test program, are both implementations valid, in which   
   case gcc is not being as aggressive in its use of move operations as   
   the standard permits, or is VC11 using move operations more   
   aggressively than it is permitted to?   
      
   Thanks,   
      
   Scott   
      
      
   [Source code below.  This is the output from VC11 beta.  Note use of moves.]   
      
   vw has size 10   
   Setting value to 0   
   Move ctor [rhs.value = 0]...   
   Move ctor [rhs.value = 0]...   
   Move ctor [rhs.value = 0]...   
   Move ctor [rhs.value = 0]...   
   Move ctor [rhs.value = 0]...   
   Move ctor [rhs.value = 0]...   
   Move ctor [rhs.value = 0]...   
   Move ctor [rhs.value = 0]...   
   Move ctor [rhs.value = 0]...   
   Move ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   vw has size 11   
      
   [Source code below.  This is the output from gcc 4.7.  Note use of copies.]   
      
   vw has size 10   
   Setting value to 0   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   Copy ctor [rhs.value = 0]...   
   vw has size 11   
      
      
      
      
   #include    
   #include    
      
   int defaultWidgetValue = -9999;   
      
      
   class Widget {   
   public:   
       static void makeVerbose() { chatter = true; }   
       static void mute() { chatter = false; }   
      
       Widget(int v = defaultWidgetValue)   
       : value(v)   
       {   
         if (chatter) std::cout << "Setting value to " << v << '\n';   
       }   
      
       Widget(const Widget& rhs)   
         : value(rhs.value)   
       {   
         if (chatter) std::cout << "Copy ctor [rhs.value = " << rhs.value <<   
   "]...\n";   
         maybeThrow();   
       }   
      
       Widget(Widget&& rhs)   
         : value(std::move(rhs.value))   
       {   
         if (chatter) std::cout << "Move ctor [rhs.value = " << rhs.value <<   
   "]...\n";   
         maybeThrow();   
       }   
      
       Widget& operator=(const Widget&)   
       {   
         if (chatter) std::cout << "Copy assignment...\n";   
         maybeThrow();   
         return *this;   
       }   
      
       Widget& operator=(Widget&&)   
       {   
         if (chatter) std::cout << "Move assignment...\n";   
         maybeThrow();   
         return *this;   
       }   
      
   private:   
       // function to prevent compiler from being able to figure out   
       // whether an exception may arise   
       void maybeThrow()   
       {   
         //    if (chatter) std::cerr << "In maybeThrow, value = " << value   
   << '\n';   
         if (value != 0) throw int();   
       }   
      
       static bool chatter;   
       int value;   
   };   
      
   bool Widget::chatter = false;   
      
   int main(int argc, char* argv[])   
   {   
       defaultWidgetValue = (argc == 1)? 0 : -1;   
      
       std::vector vw(10);   
       vw.resize(vw.capacity());   
       std::cout << "vw has size " << vw.size() << '\n';   
      
       Widget::makeVerbose();   
       Widget temp;   
       vw.push_back(temp);   
       std::cout << "vw has size " << vw.size() << '\n';   
   }   
      
      
   --   
         [ See http://www.gotw.ca/resources/clcm.htm for info about ]   
         [ comp.lang.c++.moderated.    First time posters: Do this! ]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca