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
ecan be implicitly converted to a typeTif 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