Here’s some code that tries out C++ 11 delegating constructors. These were announced as part of Visual Studio in the November CTP and avoid the need to refactor common code out of constructors into an “init” function. There are examples of their use in Stephan Levavej’s excellent video series too.
class Request { public: enum class Priority{ High, Medium, Low }; static const Priority defaultPriority(){ return Priority::Low; } // Constructor with all parameters fully specified Request( Priority priority, const std::string& requestId ) : m_priority( priority ), m_requestId( requestId ) { std::cout << "Request( Priority priority, const std::string& requestId ) called: " << "RequestId " << m_requestId << ", " << "Priority " << toString( m_priority ) << "\n"; } // Delegate to fully specified constructor above Request( const std::string& requestId ) : Request( defaultPriority(), requestId ) { std::cout << "Request( const std::string& requestId ) called: " << "RequestId " << requestId << "\n"; } // Contrived example to demonstrate chaining delegating constructors Request( const std::vector<int>& requestId ) : Request( toString( requestId ) ) { std::cout << "Request( const std::vector<int>& requestId ) called\n"; } // Contrived example to show that ~Request() is called if the delegating constructor completed Request( Priority priority ) : Request( priority, "Empty" ) { throw std::exception( "This constructor is deprecated, RequestId field is now mandatory." ); } ~Request() { std::cout << "~Request() called:" << "RequestId " << m_requestId << ", " << "Priority " << toString( m_priority ) << "\n"; } private: std::string toString( const std::vector<int>& requestId ) { std::string tmpId; for (auto elem : requestId) { if ( elem < 0 || 9 < elem ) throw std::exception("Invalid request id, should be digits"); tmpId.push_back(static_cast<char>(elem + '0')); } return tmpId; } std::string toString( Priority p ) { if (p == Priority::Low ) return "Low"; else if ( p == Priority::Medium ) return "Medium"; else if ( p == Priority::High ) return "High"; else throw std::exception( "Unexpected Priority" ); } Priority m_priority; const std::string m_requestId; };
And here’s a program that exercises the Request class (this uses musingstudio::initialize to conveniently initialize a standard container):
int _tmain(int argc, _TCHAR* argv[]) { std::cout << "\nCall fully specified constructor\n"; Request fullySpecified( Request::Priority::High, "HeartTransplant-749553" ); std::cout << "\nCall a delegating constructor\n"; Request defaultPriority( "BookDelivery-5542" ); std::cout << "\nCall chained delegating constructors\n"; Request legacy( musingstudio::initialize<std::vector<int>>( { 1, 6 } ) ); std::cout << "\nThis should throw without executing the destructor...\n"; try { // This should throw due to -ve input, but does NOT execute ~Request() // because no constructor call completed Request invalid( musingstudio::initialize<std::vector<int>>( {-1} ) ); } catch( const std::exception& exc ) { std::cout << "ERROR: " << exc.what() << std::endl; } std::cout << "\nThis should throw and execute the destructor...\n"; try { // This will throw because no requestId is not specified // (contrived example to show that ~Request() is executed // because the delegating constructor succeeded). Request noRequestId( Request::Priority::Low ); } catch( const std::exception& exc ) { std::cout << "ERROR: " << exc.what() << std::endl; } std::cout << "\nRemaining destructors...\n"; return 0; }
As with other Nov CTP features, Visual Studio intellisense hasn’t caught up yet, so expect to see red squiggly lines all over the code if you try this out.
What’s interesting is that it’s now possible for ~Request() to be called if a constructor fails to complete, as long as the delegatee (inner) constructor does complete.