Use Explicit Conversion Functions to Avert Reckless Implicit Conversions

Use Explicit Conversion Functions to Avert Reckless Implicit Conversions

he compiler invokes user-defined conversion functions (which are also called conversion operators) implicitly. In most cases, this process is well-behaved and intended. However, there are cases when you certainly don’t want the compiler to invoke the conversion operator implicitly—but you can’t prevent it. Several workarounds have been devised to mitigate this problem, including the indirect conversion idiom. And yet, these workarounds are neither intuitive nor perfect. C++0x finally brings a radical solution in the form of explicit conversion functions.

Your class defines a conversion function to a certain type. You want to ensure that this function will be invoked only when appropriate.

Declare conversion functions explicit, thus forcing clients to use casts explicitly.

Presenting the Problem
The problematic nature of unrestrained conversion functions is clearly demonstrated in smart pointers. Class std::shared_ptr defines an implicit conversion to an implementation-defined Boolean type. As you may recall, the “implementation-defined Boolean type” is actually a pointer to a data member, not the built-in booltype:

template class Ptr{private: struct PtrConversion {  int valid; };public: operator PtrConversion::*pmi() const//Boolean conversion {  return rawptr? &PtrConversion::valid : 0; }};

As ugly as this kludge may seem, it does prevent awful bugs like these:

Ptr p1, p2;cout<<"p1 + p2 = "<< p1+p2 < pf;Ptr  pq; //Query and File are unrelated typesif(pf==pq)... //compares raw pointers, not objects!

There are, however, other ailments that the indirect conversion idiom cannot cure.

Ambiguity Issues
Suppose you’re designing a simple string class. Naturally, your class defines a conversion functions to char * and an overloaded [] operator. Each of these functions has a const and a non-constversion:

class BuggyString { char buf[100];public: BuggyString(const char* s); //subscripting char& operator[] (size_t n); const char& operator[] (size_t n) const;//implicit conversion to char* operator char* (); operator const char* () const;};

When some compilers compile the following code, they issue a compilation error:

BuggyString str("buggi");str[4] = 'y'; //error, overload resolution ambiguity

Where does this error come from? When the compiler sees the expression str[4], it looks up the definition of str and discovers that str isn’t an array. At this stage, there are two possible ways to interpret the expression str[4]:

  • Invoking the overloaded [] member function.
  • Invoking BuggyString::operator char *, and then applying the built-in [] operator to the result.

In other words, depending on the underlying type of std::ptrdiff_t (which affects overload resolution) the compiler is faced with two options: either interpret str[4]as:

str.operator[](4) //call overloaded []

or interpret it as:

str.operator char*()[4]//call operator char* +built-in []

In C++03, the common approach is to avoid conversion functions in the first place. The std::string class, for example, defines the member function string::c_str() as a substitute for operator const char *. This approach works well when the destination type is known at compile time. However, when templates are involved, named functions are problematic because the destination type can only be determined when you instantiate a template.

Explicit Conversion Functions
You’re already familiar with the use of explicit in constructors that take a single argument. Unless declared explicit, a constructor C(arg) operates as implicit conversion functions that converts arg to C. C++0x extends the use of explicit to conversion functions as well. When you declare a conversion function as explicit, implicit conversions are permitted only in direct initialization expressions:

class BuggyString {//now explicit explicit operator char*(); explicit operator const char* () const;};const char * ps(str);//OK, direct initialization ps=str; //Error, explicit cast requiredps=(const char *) str; //OK, C-style cast ps=static_cast(str); //OK, new style cast

Safe and Sound
The use of explicitguarantees that conversion functions do not surprise you with undesirable conversions. Instead, every conversion operation requires an explicit permission from you in the form of at cast notation (except in the case of direct initializations). Explicit conversion functions thus offer a safe and uniform method for conversions.

Share the Post:
Heading photo, Metadata.

What is Metadata?

What is metadata? Well, It’s an odd concept to wrap your head around. Metadata is essentially the secondary layer of data that tracks details about the “regular” data. The regular

XDR solutions

The Benefits of Using XDR Solutions

Cybercriminals constantly adapt their strategies, developing newer, more powerful, and intelligent ways to attack your network. Since security professionals must innovate as well, more conventional endpoint detection solutions have evolved

AI is revolutionizing fraud detection

How AI is Revolutionizing Fraud Detection

Artificial intelligence – commonly known as AI – means a form of technology with multiple uses. As a result, it has become extremely valuable to a number of businesses across

AI innovation

Companies Leading AI Innovation in 2023

Artificial intelligence (AI) has been transforming industries and revolutionizing business operations. AI’s potential to enhance efficiency and productivity has become crucial to many businesses. As we move into 2023, several

data fivetran pricing

Fivetran Pricing Explained

One of the biggest trends of the 21st century is the massive surge in analytics. Analytics is the process of utilizing data to drive future decision-making. With so much of

kubernetes logging

Kubernetes Logging: What You Need to Know

Kubernetes from Google is one of the most popular open-source and free container management solutions made to make managing and deploying applications easier. It has a solid architecture that makes