使用用户定义的转换运算符的函数模板重载决策

function template overload resolution with user defined conversion operator

根据 C++11 标准,以下代码的正确输出是什么?

#include <iostream>

template <typename X>
class A
{
public: 
    A()
    {
        std::cout << "A::A" << std::endl;
    }
    A(const A<X>&)
    {
        std::cout << "A::A(const A<X>&)" << std::endl;
    }
    A<X>& operator = (const A<X>&)
    {
        std::cout << "A::opeartor =(conat A&)" << std::endl;
        return *this;
    }
};

void* GetData()
{
    // return data based on some condition
    static A<int> a;
    return &a;
}

class P
{

public:
    template <typename T>
    operator T&()
    {
        void* pData = GetData();             
        std::cout << "P::operator T&()" << std::endl;
        return *(reinterpret_cast<T*>(pData));
    }

    operator A<int>()
    {
        std::cout << "P::opeartor A<int>" << std::endl;
        return A<int>();
    }
};

int main(int /*argc*/, char** /*argv*/)
{
    P objP;
    A<int> objA = objP; // case 1
    objA = objP; // case 2
    return 0;
}

clang 和 gcc 产生以下输出。

P::opeartor A<int>
A::A
A::A
P::operator T&()
A::opeartor =(conat A&)

VS 2015 生成如下所示的输出。

A::A
P::operator T&()
A::A(const A<X>&)
P::operator T&()
A::opeartor =(conat A&)

案例一

VS2015 选择模板版本,而 gcc 和 clang 选择非模板版本。

案例二

所有三个编译器都选择模板版本。

我们如何参考 C++ 11 标准来解释这种行为?

MSVC 错误。这里的行为取决于目标类型是否是引用类型,这会影响候选函数集。

  • 在一个对象(A<int> objA = objP;)的复制初始化中,适用[dcl.init]/17 says that the destination type is A<int> and the candidate set is governed by [over.match.copy]中的规则,在其规则下它包括两个转换函数;他们打成平手,template/non-template 决胜局选择非模板。

  • 在初始化一个引用const A<int>&operator=的参数)时,[dcl.init.ref]/5 applies, which says that you first do overload resolution with a candidate set specified by [over.match.ref],在初始化一个对象的左值引用时,只包含转换函数返回引用。

    因此在这种情况下唯一的候选者是模板;它被证明是可行的并被选中。甚至不考虑非模板。

这也意味着 A<int> objA(objP); 将使用模板,因为您在那里对 A<int> 的构造函数进行重载解析,并将尝试初始化 const A<int>& 参数 A<int>的拷贝构造函数。