简体   繁体   中英

C++ dereference happens after implicit conversion

Today I saw that when trying to dereference an argument in a function call, the implicit conversion happens before the actual dereference operation (I believe only if the original type does not support dereference).

This observation can be seen here:

struct A{};


struct C
{
    C(const A&)
    {

    }
};


C operator*(const C&);

double f(C);


template <typename T>
struct t
{
    static const bool value = sizeof(f(**static_cast<T*>(NULL))) == sizeof(double);
};


int main(int argc, const char * argv[]) {
    t<A>::value;
}

Does the C++ standard explicitly mention this behaviour? Thank you.

Let's take a look at this expression:

f(**static_cast<A*>(NULL))

From inner-most to outer-most, static_cast<A*>(NULL) is an A* (though prefer nullptr to NULL , and also prefer using std::declval<A*>() to get "something of type A* " rather than the old casting of null pointer approach).

Next, *(a prvalue of type A*) gives you an lvalue of type A . That's just what pointer dereference means and it isn't overloadable. It's nice when things are easy to reason about.

Next, *(an lvalue of type A) . In order to figure out what that means, we transform the call to function-notation, and our set of candidates are:

The first bullet doesn't find anything, there is no A::operator*() . The second bullet, unqualified lookup on operator*() will find the function C operator*(const C&); because it is in scope. It is a viable candidate, because A is convertible to C via C(A const&) . The third bullet has no viable candidate.

As we only have one viable candidate, it's trivially the best viable candidate - so *(lvalue of type A) gives us a prvalue of type C .

To specifically answer your question:

the implicit conversion happens before the actual dereference operation

Yes, the conversion has to happen in order to resolve the dereference operation. Although it's not actually a "dereference", it's just a unary operator*() .

Lastly, f(prvalue of type C) invokes the one f we have that gives us double .


Note that in modern C++, I'd suggest writing the check as something closer to:

template <typename T>
struct t
    : std::is_same<
        decltype(f(*std::declval<T>())), // <== the type we get when we call f
                                         // on a dereferenced T
        double>
{ };

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM