C++ 取消引用发生在隐式转换之后

C++ dereference happens after implicit conversion

今天我看到当试图在函数调用中取消引用参数时,隐式转换发生在实际取消引用操作之前(我相信只有原始类型不支持取消引用)。

可以在这里看到这个观察结果:

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;
}

C++ 标准是否明确提及此行为?谢谢。

让我们看一下这个表达式:

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

从最里面到最外面,static_cast<A*>(NULL) 是一个 A*(尽管更喜欢 nullptr 而不是 NULL,并且也更喜欢使用 std::declval<A*>()获得 "something of type A*" 而不是旧的空指针方法转换)。

接下来,*(a prvalue of type A*) 给你一个 A 类型的左值。这就是指针取消引用的意思,它是不可重载的。当事情很容易推理时,这很好。

接下来,*(an lvalue of type A)。为了弄清楚这意味着什么,我们 transform 对函数符号的调用,我们的候选集是:

第一个子弹没找到,没有A::operator*()。第二个项目符号 operator*() 上的非限定查找将找到函数 C operator*(const C&);,因为它在范围内。它是一个可行的候选者,因为 A 可以通过 C(A const&) 转换为 C。第三颗子弹没有可行的候选者。

因为我们只有一个可行的候选者,所以它是最好的可行候选者 - 所以 *(lvalue of type A) 给了我们一个 C.

类型的纯右值

具体回答你的问题:

the implicit conversion happens before the actual dereference operation

是的,必须进行转换才能解决取消引用操作。虽然它实际上不是 "dereference",但它只是一个一元 operator*()

最后,f(prvalue of type C) 调用我们拥有的那个 f 给我们 double


请注意,在现代 C++ 中,我建议将检查编写为更接近于:

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>
{ };