- Threading Review/Feedback - 1 Update
- Pointer use after delete - 9 Updates
- std::condition_variable::wait_for - 3 Updates
- Initialization of static member variables in classes. - 2 Updates
- Big problem with templates - 6 Updates
- A small quiz - 1 Update
- Is new-allocation needed anymore? - 1 Update
- Is new-allocation needed anymore? - 2 Updates
Christopher Pisz <nospam@notanaddress.com>: Sep 01 03:07PM -0500 I've created a new minimal compilable example of what I want to achieve in production code. Can you guys take a look and see if I broke any rules, created deadlock situations, did things right? I am new to std::thread and std::condition_variable, not to mention C++11 vs C++98 in general. One of my concerns in whether or not I should do anything in the destructor of my ThreadedOnGoingTask and whether or not I've created any potential deadlock by my use of std::condition_variable::wait_for with shared variable g_stop. Sorry, this code is long. Threading is complicated! This really is the the most minimal compilable example I could come up with. ___ main.cpp // Project Includes #include "ThreadedOngoingTask.h" // Shared Library #include "Logger.h" // Replace with any threadsafe output // Standard Library #include <condition_variable> #include <memory> #include <mutex> #include <thread> #include <string> #include <thread> #include <vector> #include <signal.h> //-------------------------------------------------------------------------------------------------- // TODO - Only global for concept testing, later we'll wrap all this in a class std::vector<ThreadedOngoingTask::Shared_Ptr> g_workerObjects; // Tasks wrapped in a class that represent each child thread std::mutex g_threadsExitedMutex; // For locks to read/write the following two variables std::vector<std::shared_ptr<bool> > g_threadsExited; // Bool that gets set to true by a child thread immediatly before the child exits std::condition_variable g_threadsExitedCV; // Condition Variable that gets notified immediatly before a child thread exits std::mutex g_stopMutex; // For locks to read/write the following two variables bool g_stop; // Flag stops child threads std::condition_variable g_stopCV; // Condition Variable that gets notified when child threads should exit //-------------------------------------------------------------------------------------------------- void SignalHandler(int signal) { // Signal worker thread to exit const std::string msg("Halt signal caught. Exiting all threads..."); Shared::Logger::getInstance()->logExecutionEvent(msg, __FILE__, __LINE__, Shared::DateTime(), Shared::Logger::LoggingLevel::LOG_LEVEL_DEBUG); std::unique_lock<std::mutex> lock(g_stopMutex); g_stop = true; g_stopCV.notify_all(); } //-------------------------------------------------------------------------------------------------- int main() { // Register a handler for the ctrl-z signal signal(SIGINT, SignalHandler); // Create the workers and start thier threads for(size_t index = 0; index < 10; ++index) { std::shared_ptr<bool> threadExited(new bool(false)); ThreadedOngoingTask::Shared_Ptr worker(new ThreadedOngoingTask(g_threadsExitedMutex, threadExited, g_threadsExitedCV, g_stopMutex, g_stop, g_stopCV)); ThreadedOngoingTask::Start(*worker); g_threadsExited.push_back(threadExited); g_workerObjects.push_back(worker); } // Wait for all child threads to exit std::unique_lock<std::mutex> lock(g_threadsExitedMutex); while(std::any_of(g_threadsExited.begin(), g_threadsExited.end(), [](std::shared_ptr<bool> threadExited){ return !(*threadExited); })) { g_threadsExitedCV.wait(lock); } // Clean up (even though these go away at exit..for debugging) g_threadsExited.clear(); g_workerObjects.clear(); // Done return 0; } ____ ThreadedOngoingTask.h #pragma once // Standard Includes #include <atomic> #include <condition_variable> #include <memory> #include <mutex> #include <thread> //-------------------------------------------------------------------------------------------------- class ThreadedOngoingTask { public: typedef std::shared_ptr<ThreadedOngoingTask> Shared_Ptr; static void Start(ThreadedOngoingTask & ThreadedOngoingTask); ThreadedOngoingTask(std::mutex & threadsExitedMutex, std::shared_ptr<bool> threadExited, std::condition_variable & threadsExitedCV, std::mutex & stopMutex, bool & stop, std::condition_variable & stopCV); ThreadedOngoingTask(const ThreadedOngoingTask & rhs) = delete; ThreadedOngoingTask & operator = (const ThreadedOngoingTask & rhs) = delete; ~ThreadedOngoingTask(); protected: void ThreadProcedure(); static size_t m_count; size_t m_id; std::thread m_thread; // Worker thread this object will run on std::function<void()> m_threadProcedure; // Function object that is the main loop for the thread std::mutex & m_threadsExitedMutex; // Owned by parent thread. For locks to read/write the following two variables std::shared_ptr<bool> m_threadExited; // Owned by parent thread. Bool that gets set to true by a child thread immediatly before the child exits std::condition_variable & m_threadsExitedCV; // Owned by parent thread. Condition Variable that gets notified immediatly before a child thread exits std::mutex & m_stopMutex; // Owned by parent thread. For locks to read/write the following two variables bool & m_stop; // Owned by parent thread. Flag stops child threads std::condition_variable & m_stopCV; // Condition Variable that gets notified when child threads should exit }; ___ ThreadedOngoingTask.cpp #include "ThreadedOngoingTask.h" // Shared Includes #include "Logger.h" // Standard Includes #include <iostream> //-------------------------------------------------------------------------------------------------- size_t ThreadedOngoingTask::m_count = 0; //-------------------------------------------------------------------------------------------------- void ThreadedOngoingTask::Start(ThreadedOngoingTask & ThreadedOngoingTask) { // Start the thread ThreadedOngoingTask.m_thread.swap(std::thread(ThreadedOngoingTask.m_threadProcedure)); ThreadedOngoingTask.m_thread.detach(); } //-------------------------------------------------------------------------------------------------- ThreadedOngoingTask::ThreadedOngoingTask(std::mutex & threadsExitedMutex, std::shared_ptr<bool> threadExited, std::condition_variable & threadsExitedCV, std::mutex & stopMutex, bool & stop, std::condition_variable & stopCV) : m_id (m_count++), m_thread (), // Derived classes should override this behavior by binding their own thread procedure in their constructor m_threadProcedure (std::bind(&ThreadedOngoingTask::ThreadProcedure, this)), m_threadsExitedMutex(threadsExitedMutex), m_threadExited (threadExited), m_threadsExitedCV (threadsExitedCV), m_stopMutex (stopMutex), m_stop (stop), m_stopCV (stopCV) { std::ostringstream msg; msg << "ThreadedOngoingTask #" << m_id << " constructor has been called."; Shared::Logger::getInstance()->logExecutionEvent(msg.str(), __FILE__, __LINE__, Shared::DateTime(), Shared::Logger::LoggingLevel::LOG_LEVEL_DEBUG); } //-------------------------------------------------------------------------------------------------- ThreadedOngoingTask::~ThreadedOngoingTask() { std::ostringstream msg; msg << "ThreadedOngoingTask #" << m_id << " deconstructor has been called."; Shared::Logger::getInstance()->logExecutionEvent(msg.str(), __FILE__, __LINE__, Shared::DateTime(), Shared::Logger::LoggingLevel::LOG_LEVEL_DEBUG); // Note - std::thread has no mechanism of killing the thread // I suppose we are in UDB here if the object is destroyed before the thread exits // I don't know what I should do. I don't really want to flag all threads to exit by playing with the m_stop // Do I just hope we never get here before it exited and that all exceptions are caught in ThreadProcedure? } //-------------------------------------------------------------------------------------------------- void ThreadedOngoingTask::ThreadProcedure() { std::ostringstream msg; msg << "ThreadedOngoingTask #" << m_id << " thread procedure has been called."; Shared::Logger::getInstance()->logExecutionEvent(msg.str(), __FILE__, __LINE__, Shared::DateTime(), Shared::Logger::LoggingLevel::LOG_LEVEL_DEBUG); // Do not allow exceptions to escape the thread try { // Loop forever until signalled to exit std::unique_lock<std::mutex> lock(m_stopMutex); while(!m_stopCV.wait_for(lock, std::chrono::seconds(10), [this](){return m_stop;}) ) { std::ostringstream msg; msg << "ThreadedOngoingTask #" << m_id << " tick..."; Shared::Logger::getInstance()->logExecutionEvent(msg.str(), __FILE__, __LINE__, Shared::DateTime(), Shared::Logger::LoggingLevel::LOG_LEVEL_DEBUG); } } catch(...) { std::ostringstream msg; msg << "An unhandled exception occured in thread of ThreadedOngoingTask #" << m_id << ". Stopping thread."; Shared::Logger::getInstance()->logExecutionEvent(msg.str(), __FILE__, __LINE__, Shared::DateTime(), Shared::Logger::LoggingLevel::LOG_LEVEL_DEBUG); } msg.str(""); msg.clear(); msg << "ThreadedOngoingTask #" << m_id << " stopped. Exiting thread."; Shared::Logger::getInstance()->logExecutionEvent(msg.str(), __FILE__, __LINE__, Shared::DateTime(), Shared::Logger::LoggingLevel::LOG_LEVEL_DEBUG); // Notify parent the thread has exited std::unique_lock<std::mutex> lock(m_threadsExitedMutex); *m_threadExited = true; std::notify_all_at_thread_exit(m_threadsExitedCV, std::move(lock)); } -- I have chosen to troll filter/ignore all subthreads containing the words: "Rick C. Hodgins", "Flibble", and "Islam" So, I won't be able to see or respond to any such messages --- |
mark <mark@invalid.invalid>: Sep 01 12:00PM +0200 In the paper 'Preventing Use-after-free with Dangling Pointers Nullification', there is an example of Chromium using a pointer value after delete: [...] delete doc->child; allChilds[doc->child]=DELETED; [...] Is this undefined behavior? Per C++11 standard: 3.7.4.2 Deallocation functions <<< If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value (4.10), the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined. Does this imply that the example above has UB? This section is unclear to me. The "clarification" "including passing it to a deallocation function" doesn't help, since it implies a different kind of usage as opposed to just using the pointer as numeric value. |
Barry Schwarz <schwarzb@dqel.com>: Sep 01 05:34AM -0700 >after delete: >[...] >delete doc->child; This implies that doc->child is a pointer (call it T*). >allChilds[doc->child]=DELETED; How can a pointer be used as the subscript of an array (or vector)? I guess if allChilds were of type map<T*, int> and DELETED was an expression compatible with int, this might make syntactic sense. >[...] >Is this undefined behavior? Yes. Any attempt to evaluate a pointer after the memory it points to has been deallocated is UB. It is not limited to dynamic allocation either. Consider int* foo() {int i = 5; return &i;} int main() {int *p; int i; p = foo(); i = *p; // UB-1 cout << p; //also UB-1 } UB-1: You cannot dereference a pointer when you no longer own the memory it used to point to. UB-2: While not that common anymore, some hardware systems still "validate" an address even if you are not accessing the memory at that address. >to me. The "clarification" "including passing it to a deallocation >function" doesn't help, since it implies a different kind of usage as >opposed to just using the pointer as numeric value. I don't know why they added the parenthetical note. It adds nothing to the sentence. It's like saying "Any attempt to perform arithmetic (including addition) is ...". Any use of an invalid value is undefined. Note that the pointer does not become invalid until the function actually deallocates the storage. So int *p = new int; delete p; //OK delete p; //UB -- Remove del for email |
Ralf Goertz <me@myprovider.invalid>: Sep 01 02:49PM +0200 Am Tue, 01 Sep 2015 05:34:33 -0700 > UB-2: While not that common anymore, some hardware systems still > "validate" an address even if you are not accessing the memory at that > address. Really? I would have guessed there is no problem. Why should there be? A pointer is nothing but a number. If this number is used as key in a map why would any hardware try to validate the accessability of the memory that happens to be situated at that address if not being asked to? If I move to a new home and someone tries to look up my number in a phone book the guy who moved into my previous home is not being bothered by that. Only if that someone actually visits… |
scott@slp53.sl.home (Scott Lurndal): Sep 01 01:26PM >move to a new home and someone tries to look up my number in a phone >book the guy who moved into my previous home is not being bothered by >that. Only if that someone actually visits=E2=80=A6 A pointer is many things, depending on the hardware architecture. It could be a linear address (intel x86 in long mode), it could be a segment number/offset pair (intel x86 in segmented mode), it could be an 8-digit signed BCD number (burroughs B4900) or it could be a capability (burroughs B5500) or it could be an offset from the stack pointer (HP-3000). In the capability case, the pointer is an entry on the stack that cannot be written to by the application and which describes the bounds of the memory region accessible by the pointer. Been around since the early 1960's and is still supported in Unisys Clearpath systems. |
"R. Schubert" <raphael.schubert@gmx.de>: Sep 01 06:44AM -0700 On Tuesday, September 1, 2015 at 12:00:34 PM UTC+2, mark wrote: > to me. The "clarification" "including passing it to a deallocation > function" doesn't help, since it implies a different kind of usage as > opposed to just using the pointer as numeric value. Working draft version N3797 is a bit more specific: 3.7.4.2 4. [...] Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior. Any other use of an invalid pointer value has implementation-defined behavior.[38] [38] Some implementations might define that copying an invalid pointer value causes a system-generated runtime fault. It seems that, according to N3797: - If operator[] does not dereference its argument and takes it by reference, everything should be fine. - If the argument is taken by value or copied internally, you may get an error, depending on your system. But this behavior should be documented and is therefore not undefined. - If the argument is dereferenced (or passed to a deallocation function), then you get UB. From your version things are a bit muddier since I don't know your standard's definition of "using". Instinctively I would have agreed with Ralf's reasoning, but after reading the newer wording, I'm with Barry. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Sep 01 02:45PM +0100 On Tue, 1 Sep 2015 14:49:20 +0200 > asked to? If I move to a new home and someone tries to look up my > number in a phone book the guy who moved into my previous home is not > being bothered by that. Only if that someone actually visits… §3.7.4.2/4 if C++11: "If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value, the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined. [footnote: On some implementations, it causes a system-generated runtime fault.]". Copying a pointer which neither points to a valid object nor is null can apparently cause a trap on some hardware. Although applying the pointer value as the index of a map after deallocation would count as "using" it (as would, say, incrementing or decrementing it), it should be fine on x68/64, and probably on all other common architectures. It is also invalid in C apparently. Chris |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Sep 01 02:54PM +0100 On Tue, 1 Sep 2015 14:45:25 +0100 > "using" it (as would, say, incrementing or decrementing it), it should > be fine on x68/64, and probably on all other common architectures. > It is also invalid in C apparently. Ahah, as pointed out by R Schubert, C++14 is more relaxed than C++11. As opposed to C++98 and C++11, it makes copying a pointer an implementation defined matter rather than being undefined. §3.7.4.2/4 of C++14: "If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value, the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have undefined behavior. Any other use of an invalid pointer value has implementation-defined behavior. [footnote: Some implementations might define that copying an invalid pointer value causes a system-generated runtime fault.]" Thanks to him for pointing that out. Chris |
legalize+jeeves@mail.xmission.com (Richard): Sep 01 07:11PM [Please do not mail me a copy of your followup] slp53@pacbell.net spake the secret code >could be an 8-digit signed BCD number (burroughs B4900) or it could >be a capability (burroughs B5500) or it could be an offset from >the stack pointer (HP-3000). How many people under the age of 30 have even *heard* of Burroughs? I do like the examples, however :-). -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
scott@slp53.sl.home (Scott Lurndal): Sep 01 08:06PM >>be a capability (burroughs B5500) or it could be an offset from >>the stack pointer (HP-3000). >How many people under the age of 30 have even *heard* of Burroughs? The 'B' in BUNCH, none of which still remain in the computer business. https://en.wikipedia.org/wiki/BUNCH Although the burroughs name has been revived for part of the payment/check processing hardware/software that Burroughs/Unisys used to sell. |
Christopher Pisz <nospam@notanaddress.com>: Sep 01 02:05PM -0500 What kind of syntax do I use when trying to call std::condition_variable::wait_for and I have a bool variable that I want to use to control whether or not the predicate argument is true or false? I currently have while(m_stopCV.wait_for(lock, std::chrono::seconds(60), std::move(m_stop)) == std::cv_status::timeout) where m_stopCV is a std::condition_variable & lock is a local std::unique_lock m_stop is a bool & Also, I need m_stop to be protected by the mutex when it is evalutated. How do I do that while using this function? -- I have chosen to troll filter/ignore all subthreads containing the words: "Rick C. Hodgins", "Flibble", and "Islam" So, I won't be able to see or respond to any such messages --- |
Christopher Pisz <nospam@notanaddress.com>: Sep 01 02:56PM -0500 On 9/1/2015 2:05 PM, Christopher Pisz wrote: > m_stop is a bool & > Also, I need m_stop to be protected by the mutex when it is evalutated. > How do I do that while using this function? I think I got it. Lamda Expression capturing this. while(!m_stopCV.wait_for(lock, std::chrono::seconds(10), [this](){return m_stop;}) ) -- I have chosen to troll filter/ignore all subthreads containing the words: "Rick C. Hodgins", "Flibble", and "Islam" So, I won't be able to see or respond to any such messages --- |
scott@slp53.sl.home (Scott Lurndal): Sep 01 08:02PM >I currently have >while(m_stopCV.wait_for(lock, std::chrono::seconds(60), >std::move(m_stop)) == std::cv_status::timeout) The canonical way is: pthread_mutex_lock(&lock); while (!predicate) { pthread_cond_wait(&cv, &lock); /* or pthread_cond_timedwait(); */ } pthread_mutex_unlock(&lock); std::condition_variable::wait_for() does all that for you when the third (predicate) argument is a function returning bool. If you don't have a function returning bool, but rather are accessing memory directly, then you need to worry about volatile accesses to the predicate vis-a-vis compiler optimizations. |
Paul <pepstein5@gmail.com>: Sep 01 02:24AM -0700 I understand that, for many years (since about 03 perhaps), it's been fine to write class SomeClass {// const static int x = 3; // }; More recently, (C++ 09 I think) we have been able to write class SomeClass { // int x = 3; // }; What I haven't been able to find out is what happens if the member variable is static and non-constant? Can we write the below? Thank you, Paul class SomeClass { // static int x = 3; // }; |
legalize+jeeves@mail.xmission.com (Richard): Sep 01 07:08PM [Please do not mail me a copy of your followup] Paul <pepstein5@gmail.com> spake the secret code >What I haven't been able to find out is what happens if the member >variable is static and non-constant? As I understand it, it is a shorthand for initializing non-static members of a class. >Can we write the below? shell 179> g++ -std=c++0x /tmp/a.cpp /tmp/a.cpp:7:17: error: ISO C++ forbids in-class initialization of non-const static member 'SomeClass::x' static int x = 3; ^ shell 180> cat /tmp/a.cpp #include <cassert> class SomeClass { public: // static int x = 3; // }; int main() { assert(SomeClass::x == 3); SomeClass s; assert(SomeClass::x == 3); return 0; } -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
Juha Nieminen <nospam@thanks.invalid>: Sep 01 08:17AM > Should I abstain from asking questions here? I think you should abstain from an attitude of the sort "I don't like this language, therefore I'll make provocative passive-aggressive posts in the newsgroup of that language, and get a hissy-fit when I get responded in kind." Do you at the very least agree that C is not a panacea, and that there are tons and tons of C projects out there that are at least as much of a mess as your C++ project? Take any big open source C project out there, and there's at least 50% chance that it will be absolutely horrible design-wise, will be horrendously inefficient, will have at least one memory leak, and will most probably not compile without warnings, or even errors. --- news://freenews.netfront.net/ - complaints: news@netfront.net --- |
jacobnavia <jacob@jacob.remcomp.fr>: Sep 01 12:17PM +0200 Le 01/09/2015 10:17, Juha Nieminen a écrit : > Do you at the very least agree that C is not a panacea, and that there > are tons and tons of C projects out there that are at least as much of > a mess as your C++ project? Of course I agree SIR! (Otherwise I would be excluded from this group, sigh:-) > chance that it will be absolutely horrible design-wise, will be horrendously > inefficient, will have at least one memory leak, and will most probably > not compile without warnings, or even errors. YES SIR! jacob :-) P.S. You will agree however that debugging C code is orders of magnitude easier than debugging templated C++ code... Well if you do not mind SIR! |
Juha Nieminen <nospam@thanks.invalid>: Sep 01 11:18AM > jacob :-) > P.S. You will agree however that debugging C code is orders of magnitude > easier than debugging templated C++ code... Well if you do not mind SIR! Ok, you are now officially an asshole. --- news://freenews.netfront.net/ - complaints: news@netfront.net --- |
jacobnavia <jacob@jacob.remcomp.fr>: Sep 01 02:42PM +0200 Le 01/09/2015 13:18, Juha Nieminen a écrit : > Ok, you are now officially an asshole. This shows the education of Mr Nieminen and his attitude towards people that do not have the same programming language tastes. I do not call people names, Mr Nieminen, it is just bad taste. jacob |
Gareth Owen <gwowen@gmail.com>: Sep 01 03:59PM +0100 > language, therefore I'll make provocative passive-aggressive posts in the > newsgroup of that language, and get a hissy-fit when I get responded in > kind." Heh. Good luck with that. |
legalize+jeeves@mail.xmission.com (Richard): Sep 01 07:05PM [Please do not mail me a copy of your followup] jacob@jacob.remcomp.fr spake the secret code >In 2010, tokens and identifiers were resolved at template expansion >time. Somehow this behaviour changed between 2010 and today and now I am >getting hundreds of undefined references in those templates. Please post one specific example of your problem. Change identifiers/whatever if you feel you need to protect your source code. Reproducing the problem in a small scope always helps me understand the problem better. Sometimes, the problem isn't what I thought it was at all. -- "The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline> The Computer Graphics Museum <http://computergraphicsmuseum.org> The Terminals Wiki <http://terminals.classiccmp.org> Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com> |
mark <mark@invalid.invalid>: Sep 01 03:28PM +0200 Another obscure language feature - defined in C++14 7.1.6.2/4.3. Thankfully, gcc, clang and Visual C++ all issue warnings about returning a reference to a local variable. |
ram@zedat.fu-berlin.de (Stefan Ram): Sep 01 12:01PM >template< class T, typename S >::std::unique_ptr< T >make_unique( S x ) >{ return( ::std::unique_ptr< T >(new T( my_forward< S >( x )))); } And then, for two arguments: template< class T, typename S, typename U > ::std::unique_ptr< T >make_unique( S x, U y ) { return ( ::std::unique_ptr< T > ( new T( my_forward< S >( x ), my_forward< U >( y )))); } (untested). >template< typename T >T && my_forward( T && x ) >{ return static_cast< T&& >( x ); }. Such a »my_forward« would only be needed to replace »::std::forward« if one does not have »::std::forward«. |
jt@toerring.de (Jens Thoms Toerring): Aug 31 11:41PM > > mention, they can't be used as buffers the same way as vectors. > Yes they can - by using slightly uglier syntax &string[0] (or (string.empty > ()? NULL: &string[0]) instead of string.data(). I'll take your word for it;-) Thank you and best regards, Jens -- \ Jens Thoms Toerring ___ jt@toerring.de \__________________________ http://toerring.de |
Paavo Helde <myfirstname@osa.pri.ee>: Sep 01 12:32AM -0500 jt@toerring.de (Jens Thoms Toerring) wrote in >> Yes they can - by using slightly uglier syntax &string[0] (or >> (string.empty ()? NULL: &string[0]) instead of string.data(). > I'll take your word for it;-) Thank you, but there is no need to take my word ;-) These are the relevant quotes from the standard: 21.4.1 basic_string general requirements 5. The char-like objects in a basic_string object shall be stored contiguously. That is, for any basic_string object s, the identity &*(s.begin() + n) == &*s.begin() + n shall hold for all values of n such that 0<= n < s.size(). 21.4.5 basic_string element access const_reference operator[](size_type pos) const; reference operator[](size_type pos); 2. Returns: *(begin() + pos) if pos < size(). Cheers Paavo |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment