Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 std::is_convertible behaviour with private copy constructor

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.

like image 587
Matei David Avatar asked Apr 11 '14 05:04

Matei David


1 Answers

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 type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t.


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&.

like image 196
dyp Avatar answered Oct 24 '22 00:10

dyp