Поведение C++11 std::is_convertible с частным конструктором копирования
-
20-12-2019 - |
Вопрос
Я пытаюсь понять std::is_convertible
в С++11.В соответствии с cppreference.com, std::is_convertible<T,U>::value
должно оцениваться как 1, если и только если "Если мнимое значение типа T
может использоваться в операторе return функции, возвращающей U
".Однако в формулировке ничего не говорится о том, где может быть объявлена эта функция.Чего следует ожидать, когда конструктор копирования U
является частным?Чего следует ожидать, когда T
это ссылка на lvalue?
Например, рассмотрим этот код:
#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
}
я использую gcc-4.8.2
или clang-3.4
(никакой разницы в выводе), и я компилирую с помощью:
{g++|clang++} -std=c++11 -Wall -Wextra eg.cpp -o eg
Здесь, std::is_convertible< Ref_A, A >
отчеты 0
.Однако вы можете видеть это Fact_A::f
возвращает объект типа A
, и значение типа r Ref_A
используется в его операторе возврата.Проблема в том, что конструктор копирования A
является private
, поэтому эту функцию нельзя разместить где-либо еще.Правильно ли текущее поведение по отношению к стандарту?
Второй вопрос.Если я удалю private
, вывод превращается в 1 1 1
.Что означает последний 1
иметь в виду?Что такое «rvalue типа A&
"?Это ссылка на rvalue?Поскольку вы могли заметить, что я явно удалил конструктор перемещения A
.В связи с этим я не могу объявить Fact_A::g
.Но все равно, std::is_convertible< A&, A >
отчеты 1
.
Решение
is_convertible
определяется следующим образом в [meta.rel]/4 из n3485:
Учитывая следующий прототип функции:
template <class T> typename add_rvalue_reference<T>::type create();
условие предиката для специализации шаблона
is_convertible<From, To>
Должны быть удовлетворены тогда и только тогда, когда выражение возврата в следующем коде будет хорошо сформировано, включая любые неявные преобразования в тип возврата функции:To test() { return create<From>(); }
и здесь вам нужен перемещаемый/копируемый To
:Оператор return применяет неявное преобразование, и для этого требуется доступный конструктор копирования/перемещения, если To
это тип класса (T&
не является типом класса).
Сравните с [conv]/3
Выражение
e
может быть неявно преобразовано к типуT
тогда и только тогда, когда объявлениеT t=e;
правильно сформирован для некоторой изобретенной временной переменнойt
.
Если From
является T&
, вы получите что-то вроде
To test() {
return create<T&>();
}
который, подобно std::declval
, является lvalue:Выражение create<T&>()
is/дает lvalue, так как T& &&
(с помощью add_rvalue_reference
) свернуто в T&
.