Why is the Visual Studio compiler happy with
void fn(int *&i)
{
;
}
and
void fn(IUnknown *const &p)
{
;
}
but not
void fn(IUnkown *&p)
{
;
}
where calling it looks like
IDXGIFactory *df = nullptr;
// init df
fn(df);
compiler error is
3 IntelliSense: a reference of type "IUnknown *&" (not const-qualified) cannot be initialized with a value of type "IDXGIFactory *" c:\Users\Carl\Documents\Visual Studio 2013\Projects\Project1\Project5\main.cpp 29 10 Project5
The closest thing I've dug up with research is that the compiler will only do one type conversion at a time, but that can't be right because then the const & version should break from doing a type and const conversion; however it is the & version that actually won't compile.
A non-const lvalue reference (like IUnknown*&
) can only bind to an lvalue; it cannot bind to an rvalue. A const-qualified lvalue reference (like IUnknown* const&
) can bind to an rvalue.
It's easier to consider a simpler case that does not involve pointers or function calls:
int i = 0;
double x = i; // (1) Well-formed
double const& y = i; // (2) Well-formed
double& z = i; // (3) Ill-formed
Here, i
is an object of type int
. When i
is used in an expression, it is an lvalue.
In (1), we initialize the object x
(of type double
) from i
(of type int
). The type does not match, but this is okay, because there is an implicit conversion from int
to double
. The "result" of this conversion is an rvalue expression(*) of type double
, which is used to initialize x
.
In (2), we initialize the const-qualified reference y
(of type double const&
) from i
. Again, the types do not match, so the implicit conversion is used to convert the int
to double
. The "result" of this conversion is an rvalue. As noted at the beginning, a const-qualified reference can bind to an rvalue, so y
is bound to the "result" of the conversion.
In (3), we attempt to initialize the non-const reference z
(of type double&
) from i
. The types do not match, so a conversion would be required. The conversion cannot be used here, because the "result" of the conversion is an rvalue, and as noted at the beginning, a non-const reference cannot bind to an rvalue.
C++ has special rules to permit const lvalue references to bind to rvalue expressions. You can find out why from other questions here on StackOverflow, like "How come a non-const reference cannot bind to a temporary object?"
Your case is exactly the same as this one: the type of your argument (IDXGIFactory*
) is not the same as the type of the parameter (IUnknown*
or a reference thereto), so an implicit conversion is required to convert the argument to the parameter type (in this case, it's a conversion from a pointer to a derived class to a pointer to a base class). The "result" of this conversion is an rvalue expression, however, so it cannot bind to the non-const reference IUnknown*&
.
(*)It's really a prvalue; I've used the C++98 expression taxonomy in this answer for simplicity. See this question for information about the C++11 value categories.