带有用户定义转换运算符的函数模板重载解析

3
根据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&)

案例1

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

案例2

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

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


如果不确定,就假设 VS 不符合要求。 :) - erip
1个回答

3

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

  • 在对象的复制初始化中(A<int> objA = objP;),适用于[dcl.init]/17中的规则指定目标类型为A<int>,候选集由[over.match.copy]控制,根据它的规则,它包括两个转换函数;它们被绑定在一起,模板/非模板的抉择器选择非模板。

  • 在初始化引用const A<int>&(即operator=的参数)时,应用[dcl.init.ref]/5,它说你首先对候选集进行重载解析,该候选集由[over.match.ref]指定,当初始化左值引用到对象时,仅包括返回引用的转换函数。

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

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


1
要指出一个细节:初始化引用时,首选使引用直接绑定的转换函数。也就是说,如果转换函数模板不存在,则选择operator A<int>。这是在直接/间接绑定和重载解析之间进行的两步过程。 - dyp
(虽然我不太确定倒数第二个符号点“如果T1或T2是类类型[...]”是否被视为直接绑定。) - dyp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接