Explicit Specifier in C++ (ENG)

Intro to Explicit

In some official code, you may see the explicit specifier used inside a class, especially when specifying a constructor. Since C++11, this specifier is also be used for conversion functions and deduction guides. Today, I will take a peek at how this specifier works inside a class.

Before we delve into the technical details, let's first understand the word "explicit."

Explicit and Implicit in Dictionary

In the dictionary, the word "explicit" in English is barrowed from the French loanword "explicite" from the 1610s. It originally comes from the Latin word "explicitus" meaning "unobstructed", which is the variant past participle of the Latin verb "explicare" original meaning "unfold, explain".

The word "explicit" means "open to understanding, specific, clear" nowadays, which is similar to the old Latin "explicare," meaning "to unfold." You might say, "why don't you unfold/explain your language" to ask for a specific answer (equals to "why don't you explicate your language"). In Chinese, you may call it "显性、明确", indicating something should be obvious or specific.

The opposite "implicit" means "vogue, no specific", which refers to something implied or understood without being directly stated.

Jargon Stuff Now!

In C++, you may have heard about "implicit conversions" or "implicit casts," which act as follows:

class MyClass {
public:
    MyClass(int x) {
        // Constructor logic
    }
};

void someFunction(MyClass obj) {
    // Function logic
}

int main() {
    someFunction(42); // Implicitly converts 42 to MyClass using MyClass(int)
    return 0;
}

Without the explicit specifier, the compiler will do implicit conversions. Meaning the compiler will automatically convert the input into a type-specific class or type, which can lead to unintended behavior because of the uncertainty and lack of definition.

To prevent implicit conversions and ensure that conversions are type-specific and intentional, you can use the explicit keyword. Here’s how it works:

class MyClass {
public:
    explicit MyClass(int x) {
        // Constructor logic
    }
};

void someFunction(MyClass obj) {
    // Function logic
}

int main() {
    // someFunction(42); // Error: cannot convert int to MyClass implicitly
    someFunction(MyClass(42)); // OK: Explicitly create a MyClass object
    return 0;
}

By marking the constructor as explicit, you prevent unintended implicit conversions and ensure that object creation is always intentional and clear. This helps avoid errors and unexpected behavior in more complex scenarios.