После добавления кода для точного определения преобразований в исходном коде я получаю следующее:
Результат
Template conversion
is_constructible<1A, 1C>
call non-template conversion: 1D->1C
return a 1C as a 1A
Non-template conversion: D -> C
Template conversion
is_constructible<1A, 1B>
call non-template conversion: 1C->1B
return a 1B as a 1A
Non-template conversion: C -> B
Non-template conversion: B -> A
Шаг 1 :
После ручного перемещения кода из indirect_conversion
в struct D
я получаю следующее:
struct D {
template<typename T, typename = std::enable_if_t<
std::is_constructible_v<T, C>
>>
operator T() {
return static_cast<D*>(this)->operator C();
}
....
Основная функция запрашивает неявное преобразование из D
в A
.
Предположим, C
является конструктивным из A
.
Поскольку C
является конструктивным из A
, включите оператор неявного преобразования из D
в A
.
Определить неявное преобразование из D
в A
как:
- Явно преобразует из
D
в C
на static_cast<D*>(this)->operator C();
.
- Это
Non-template: D -> C
в результате.
- Запросить неявное преобразование из
C
в A
- Он запрашивает неявное преобразование, потому что оператор return возвращает
C
, но тип возврата T
равен A
.
Остальные шаги аналогичны.
Объяснение предположения
Последний раздел объясняет 1-й шаг. Все шаги вместе объясняют первоначальное предположение, что C
является конструктивным из A
.
B
является конструктивным из A
. Это значит:
- Оператор преобразования из
C
в A
включен. Это означает, что C
можно построить из A
.
- Это означает, что оператор преобразования из
D
в A
включен. Это означает, что D
является конструктивным из A
.
Модифицированный код для тестирования :
#include <type_traits>
#include <iostream>
template <typename T1, typename T2>
struct indirect_conversion {
template <typename T,
typename = std::enable_if_t<
std::is_constructible_v<T, T2>
>
>
operator T() {
auto T_name = typeid(T).name();
auto T1_name = typeid(T1).name();
auto T2_name = typeid(T2).name();
std::cout << "Template conversion" << "\n";
std::cout << " is_constructible<"
<< T_name << ", "
<< T2_name << ">\n";
std::cout << " call non-template conversion: "
<< T1_name << "->" << T2_name << "\n";
std::cout << " return a " << T2_name << " as a " << T_name << "\n";
return static_cast<T1 *>(this)->operator T2();
}
};
struct A {};
struct B : indirect_conversion<B, A> {
operator A() {
std::cout << "Non-template conversion: B -> A\n";
return A();
}
};
struct C : indirect_conversion<C, B> {
operator B() {
std::cout << "Non-template conversion: C -> B\n";
return B();
}
};
struct D : indirect_conversion<D, C> {
operator C() {
std::cout << "Non-template conversion: D -> C\n";
return C();
}
};
int main() {
A a = D();
}