TLDR: У меня есть два шаблонных класса Outer
а также Inner
, Inner<X>
может быть неявно построен из X
, а также Outer<Y>
может быть неявно построен из Y
, Должен Outer<Inner<X>> = X()
Работа?
Больше деталей:
Предположим, у меня есть следующие два класса:
template<typename T>
class Inner {
public:
Inner(const T& value) {}
Inner(T&& value) {}
};
template<typename T>
class Outer {
public:
Outer(const T& value) {}
Outer(T&& value) {}
};
Рассмотрим следующую функцию:
struct SomeType{};
Outer<Inner<SomeType>> DoSomethingFails() {
SomeType value;
return value;
}
G ++ жалуется:
no viable conversion from 'SomeType' to 'Outer<Inner<SomeType> >'
note: candidate constructor not viable: no known conversion from 'SomeType' to 'const Inner<SomeType> &' for 1st argument
Но если я сделаю следующее вместо этого:
Outer<Inner<SomeType>> DoSomethingWorks() {
SomeType value;
return Inner<SomeType>(value);
}
Оно работает. Разумно ли ожидать DoSomethingFails
работать? Если нет, то почему? И может ли код быть изменен таким образом, чтобы DoSomethingFails
работает?
Ваш первый пример требует двух пользовательских преобразований для компиляции — SomeType -> Inner -> Outer
, Однако самое большее одно пользовательское преобразование может быть применено неявно.
Цитируя N3337, §12.3 [class.conv]
1 Преобразования типов объектов класса могут быть определены конструкторами и функциями преобразования. Эти преобразования называются пользовательскими преобразованиями и используются для неявных преобразований типов (раздел 4), для инициализации (8.5) и для явных преобразований типов (5.4, 5.2.9).
4 Не более одного пользовательского преобразования (конструктор или функция преобразования) неявно применяется к одному значению.
Если цель состоит в том, чтобы избежать упоминания Inner<SomeType>
в операторе возврата вы можете использовать инициализацию списка.
Outer<Inner<SomeType>> DoSomethingWorks2() {
SomeType value;
return {std::move(value)};
}