I'm trying to understand std::is_convertible
in C++11. According to cppreference.com, std::is_convertible<T,U>::value
should evaluate to 1 iff "If an imaginary rvalue of type T
can be used in the return statement of a function returning U
". The wording says nothing about where that function might be declared, though. What should one expect when the copy constructor of U
is private? What should one expect when T
is an lvalue reference?
E.g., consider this code:
#include <iostream>
#include <type_traits>
struct Fact_A;
struct A {
friend struct Fact_A;
A() = default;
A(A&&) = delete;
private:
A(const A&) = default;
};
struct Ref_A {
A* _ptr;
Ref_A(A* ptr) : _ptr(ptr) {}
operator A& () { return *_ptr; }
};
struct Fact_A {
static A* make_A(const A& a) { return new A(a); }
static A f(A* a_ptr) { return Ref_A(a_ptr); }
//static A g(A&& a) { return std::move(a); }
};
int main() {
A a1;
A* a2_ptr = Fact_A::make_A(a1);
(void)a2_ptr;
std::cout << std::is_convertible< Ref_A, A >::value << "\n" // => 0
<< std::is_convertible< Ref_A, A& >::value << "\n" // => 1
<< std::is_convertible< A&, A >::value << "\n"; // => 0
}
I'm using gcc-4.8.2
or clang-3.4
(no difference in output), and I compile with:
{g++|clang++} -std=c++11 -Wall -Wextra eg.cpp -o eg
Here, std::is_convertible< Ref_A, A >
reports 0
. However, you can see that Fact_A::f
returns an object of type A
, and an rvalue of type Ref_A
is used in its return statement. The problem is that the copy constructor of A
is private
, so that function cannot be placed anywhere else. Is the current behaviour correct with respect to the standard?
Second question. If I remove private
, the output turns into 1 1 1
. What does the last 1
mean? What is an "rvalue of type A&
"? Is that an rvalue reference? Because you might notice I explicitly deleted the move constructor of A
. As a result of this, I cannot declare Fact_A::g
. But still, std::is_convertible< A&, A >
reports 1
.
is_convertible
is defined as follows in [meta.rel]/4 from n3485:
Given the following function prototype:
template <class T> typename add_rvalue_reference<T>::type create();
the predicate condition for a template specialization
is_convertible<From, To>
shall be satisfied if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function:To test() { return create<From>(); }
and here, you need a movable/copyable To
: The return-statement applies an implicit conversion, and this requires an accessible copy/move constructor if To
is a class type (T&
is not a class type).
Compare to [conv]/3
An expression
e
can be implicitly converted to a typeT
if and only if the declarationT t=e;
is well-formed, for some invented temporary variablet
.
If From
is T&
, you get something like
To test() {
return create<T&>();
}
which, similar to std::declval
, is an lvalue: The expression create<T&>()
is/yields an lvalue, since T& &&
(via add_rvalue_reference
) is collapsed to T&
.
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