What is the correct output for following code according to C++11 standard?
#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 and gcc produce following output.
P::opeartor A<int>
A::A
A::A
P::operator T&()
A::opeartor =(conat A&)
VS 2015 generates output as shown below.
A::A
P::operator T&()
A::A(const A<X>&)
P::operator T&()
A::opeartor =(conat A&)
Case 1
VS2015 picks template version where as gcc and clang picks non template version.
Case 2
All three compilers pick template version.
How can we explain this behavior with reference to C++ 11 standard?
MSVC is wrong. The behavior here depends on whether the destination type is a reference type, which affects the set of candidate functions.
In copy-initialization of an object (A<int> objA = objP;), the applicable rules in [dcl.init]/17 says that the destination type is A<int> and the candidate set is governed by [over.match.copy], under whose rules that it includes both conversion functions; they are tied, and the template/non-template tiebreaker selects the non-template.
In initializing a reference const A<int>& (the parameter of operator=), [dcl.init.ref]/5 applies, which says that you first do overload resolution with a candidate set specified by [over.match.ref], which, when initializing an lvalue reference to object, includes only conversion function returning references.
Thus in this case the only candidate is the template; it proves viable and is selected. The non-template isn't even considered.
This also means that A<int> objA(objP); will use the template, because there you do overload resolution on the constructors of A<int>, and will be trying to initialize the const A<int>& parameter of A<int>'s copy constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With