- Overview
- Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
- Tips and Techniques
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
- C++0X: The New Face of Standard C++
- Reference Wrapper
- The Performance Technical Report
- auto for the People
- Ironing Templates' Syntactic Wrinkles
- Visual C++ Becomes ISO Compliant
- A Garbage Collector for C++
- C99 Core Features in C++0X
- The
shared_ptrClass - The shared_ptr Class, II
- Lambda Expressions and Closures, Part I
- Lambda Expressions and Closures, Part II
- Lambda Expressions and Closures, Part III
- The Type Traits Library, Part I
- The Type Traits Library, Part II
- The Type Traits Library, Part III
- finally Revisited
- The Any Library
- The nullptr Keyword Proposal
- Delegating Constructors
- The Explicit Conversion Operators Proposal
- Conditionally-Supported Behavior
- The weak ptr Class Template, Part I
- The weak ptr Class Template, Part II
- POD Types Revisited
- The rvalue Reference Proposal, Part I
- The rvalue Reference Proposal, Part II
- Proposal for New String Algorithms
- Concepts, Part I
- Concepts, Part II
- constexpr: Generalized Constant Expressions
- The constexpr Proposal: Constructors
- Strongly-Typed enum Types
- C++09: The Road Ahead
- C++09: Proposals by Statuses
- Changing Undefined Behavior to Diagnosable Errors
- New Character Types
- The __func__ Predeclared Identifier is Coming to C++
- Static Assertions
- The extern template Proposal
- Variadic Templates, Part I
- Variadic Templates, Part II
- Variadic Templates, Part III -- Critique
- Using unique_ptr, Part I
- Using unique_ptr, Part II
- Unrestricted Unions, Part I
- Unrestricted Unions, Part II
- Unrestricted Unions, Part III
- Types With No Linkage as Template Arguments
- New Initialization Syntax
- Initializer Lists and Sequence Constructors
- New Standard Library Algorithms
- Class Member Initializers
- Inheriting Constructors
- Introducing Attributes
- The Removal of Concepts From C++0x
- The Future of C++0x, Part I
- The Future of C++0X, Part II
- The Debate About Attributes, Part I
- The Debate About Attributes, Part II
- The Debate About Attributes, Part III
- The Debate About Attributes, Part IV
- Forward Declarations of Enum Types
- The SCARY Iterators Proposal, Part I
- The SCARY Iterators Proposal, Part II
- Heading for Deprecation: export, Exception Specification and register
- The Rejection of the Unified Function Syntax Proposal
- Rvalue References as Object Members
- FCD Approved
- The Debate on noexcept, Part I
- The Debate on noexcept, Part II
- The Debate on noexcept, Part III
- About-face -- [[Attributes]] to Be Replaced with Keywords
- Will Delegating Constructors Be Removed From C++0x?
- Rvalue References: Past, Present and Future, Part I
- Rvalue References: Past, Present and Future, Part II
- Rvalue References: Past, Present and Future, Part III
- A Move in the Right Direction, Part I
- A Move in the Right Direction, Part II
- New Keywords for Inheritance Control, Part I
- New Keywords for Inheritance Control, Part II
- FDIS Approved
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
The Explicit Conversion Operators Proposal
Last updated Jul 21, 2006.
Many of the new C++0X core features, including delegating constructors and nullptr are already finalized. However, there are other proposals in the standardization pipeline which are still being developed. One of them is N1592: a proposal to add explicit conversion operators to C++. Learn what the problem with C++98 conversion operators is, how the new proposal attempts to fix it, and why I’m not particularly enthusiastic about it.
Implicit Conversions: Convenient but Dangerous
As we already know, a conversion operator implicitly converts objects of its class type to the target type. For example, a smart pointer class often defines a conversion operator that allows users to use smart pointer objects in a context that requires bool:
template <class T>
class Ptr
{
// stuff
public:
operator bool() const
{
return (rawptr ? true: false);
}
private:
T * rawptr;
};
Ptr<int> ptr( &var );
if(ptr ) //calls conversion operator
{
// the pointer is valid
}
//...
In this example, the automatic conversion to bool is well-behaved. However, once you permit an implicit conversion, you may find that it could make your compiler accept nonsensical code such as this:
Ptr<int> p1; Ptr<double> p2; cout << "p1 + p2 = " << p1 + p2 << endl; // prints 0, 1, or 2 Ptr<File> sp1; Ptr<Query> sp2; // Query and File are unrelated types if (sp1 == sp2) // Converts both pointers to bool and compares results
The upshot is that while conversion operators are handy, they compromise type safety and disarm the compiler of its type-safety checks. For this reason, library designers either forgo conversion operators at the cost of inconveniencing users (the lack of a const char * conversion operator in std::string is a classic example of this), or they resort to embarrassingly contrived constructs to support this notion without its dangerous consequences.
One such contrived construct consists of replacing the target type bool with a pointer to a data member of an embedded struct:
template <class T>
class Ptr
{
public:
struct PointerConversion
{
int valid;
};
typedef int PointerConversion::* datamemptr;
operator datamemptr ()const //a tamed version of operator bool
{
return rawptr? &PointerConversion::valid : 0;
}
};
If you find this code awkward and unreadable, I agree. I don’t know how many C++ programmers can decipher pointer to data members syntax. Even for those who can, the idea of using a pointer to a data member of an embedded ad hoc struct for the purpose of restricting an implicit conversion to bool would sound odd, to say the least. However, such code does get written. The boost::shared_ptr class uses a similar implementation to support an implicit conversion to bool which is free from unintended conversion to arithmetic types.
Converting Constructors
Non-explicit constructors taking one argument cause a similar problem. Every such constructor of a class called X functions as an implicit conversion operator, converting an argument of type Y to X:
class U{};
class T
{
public:
T( U const & ); // implicitly converts U to T
};
U u;
T t;
t=u; //u implicitly converted to T
In the case of converting constructors, you can disable implicit conversions by declaring the constructor as explicit The N1592 proposal stretches the semantics of this keyword to all conversion operators. A conversion operator declared explicit will not perform an implicit conversion. Instead, the programmer will have to call it explicitly:
class U{}; class S{};
class T{
public:
operator U { return u;}//implicit
explicit operator S {return S();}
private:
U u;
};
T t;
U u;
S s;
u=t; //OK, implicit conversion operator used
u=s; //error, no implicit conversion from S to U
u=static_cast<U&> (s); //OK, explicit conversion used
u= (U)s; //OK, C-style cast
An explicit conversion operator is merely a uniform naming convention that does the same job of an older concept, namely: a member function that performs an explicit conversion. In other words, instead of calling string::c_str() to convert a string object to const char *, the proposed feature allows you to replace c_str() with:
class MyString
{
public:
explicit operator const char *() const;
};
However, the explicit conversion operator offers one advantage over the traditional conversion functions. In generic programming, one often needs to convert objects whose type is unknown at coding time. An explicit conversion operator can be generic as well, using a parameterized destination type:
explicit operator const T *() const;
The proposal also includes another generic conversion idiom based on namespaces and a template member function with an agreed-upon name:
class M
{
public:
template <class To>
operator To()
{
return static_cast<To>( *this );
}
};
Conclusions
In the Berlin meeting of April 2006, this proposal was accepted by the Evolution group and was moved to Core for "further scrutiny". However, if you look examine the carefully you will notice that this proposal is still far from final. There’s no doubt that the current semantics of conversion operators is too permissive. However, I believe that combining namespaces and an agreed-upon function name is wrong. Conversion operators were added to C++ to avoid unwieldy and complicated syntactic beasts. This proposal (i.e., the namespace based solution) makes conversion operators overly arcane and verbose. As for the proposed explicit conversion operators, e.g.,
explicit operator const T *() const;
They are slightly better since they are intuitive and simple to use. However, I suspect that once this construct becomes standardized, it will be overused by defensive programming zealots. Obviously, this will lead users to tamper with standard classes (e.g., deriving from concrete classes that lack a virtual destructor) only to override an explicit conversion operator with an implicit one. Perhaps boost::shared_ptr designers were right all along...



