Неявное преобразование, не происходящее
-
28-09-2019 - |
Вопрос
Последний вопрос, который я спросил, было то, на что я наткнулся, пытаясь понять еще одну вещь ... что я тоже не могу понять (не мой день).
Это довольно длительное утверждение вопроса, но, по крайней мере, я надеюсь, что этот вопрос может доказать полезное для многих людей, а не только мне.
Код у меня есть следующее:
template <typename T> class V;
template <typename T> class S;
template <typename T>
class V
{
public:
T x;
explicit V(const T & _x)
:x(_x){}
V(const S<T> & s)
:x(s.x){}
};
template <typename T>
class S
{
public:
T &x;
explicit S(V<T> & v)
:x(v.x)
{}
};
template <typename T>
V<T> operator+(const V<T> & a, const V<T> & b)
{
return V<T>(a.x + b.x);
}
int main()
{
V<float> a(1);
V<float> b(2);
S<float> c( b );
b = a + V<float>(c); // 1 -- compiles
b = a + c; // 2 -- fails
b = c; // 3 -- compiles
return 0;
}
Выражения 1 и 3 отлично работают, в то время как выражение 2 не компилируется.
Если я понял должным образом, что происходит:
Выражение 1.
- слияние неявно преобразуется в
const
используя стандартную последовательность преобразования (состоящую только на одном Преобразование квалификации). V<float>(const S<T> & s)
называется и временныйconst V<float>
Объект сгенерирован (давайте назовем это т). Это уже констота, потому что это временное значение.- а. преобразуется в постоянный аналогично слияние.
operator+(const V<float> & a, const V<float> & b)
вызывается, в результате чего в результате временного типаconst V<float>
что мы можем позвонить Q..- по умолчанию
V<float>::operator=(const & V<float>)
называется.
Я в порядке здесь? Если я сделал даже самую тонкую ошибку, пожалуйста, дайте мне знать, потому что я пытаюсь получить понимание о неявном кастине как можно глубину ...
Выражение 3.
- слияние преобразуется в
V<float>
. Отказ Для этого у нас есть пользовательская последовательность преобразования:
1.1. Первое стандартное преобразование:S<float>
кconst S<float>
через преобразование квалификации.
1.2. Определенное пользователем преобразование:const S<float>
кV<float>
черезV<float>(const S<T> & s)
конструктор.
1.3 Второе стандартное преобразование:V<float>
кconst V<float>
через преобразование квалификации. - по умолчанию
V<float>::operator=(const & V<float>)
называется.
Выражение 2?
То, что я не понимаю, это то, почему есть проблема со вторым выражением. Почему следующая последовательность невозможна?
- слияние преобразуется в
V<float>
. Отказ Для этого у нас есть пользовательская последовательность преобразования:
1.1. Исходное стандартное преобразование:S<float>
кconst S<float>
через преобразование квалификации.
1.2. Определенное пользователем преобразование:const S<float>
кV<float>
черезV<float>(const S<T> & s)
конструктор.
1.3. Окончательное стандартное преобразование:V<float>
кconst V<float>
через преобразование квалификации. - Шаги 2-6 такие же, как в случае выражения 1.
Прочитав стандарт C ++, хотя: «Эй! Может быть, проблема пришла к 13.3.3.1.2.3! Какие государства:
Если пользовательское преобразование определяется функцией преобразования шаблона, вторая стандартная последовательность преобразования должна иметь точное ранг совпадения.
Но это не может быть так, поскольку преобразование квалификации имеет точный ранг совпадения ...
У меня действительно нет подсказки ...
Ну, есть ли у вас ответ или нет, спасибо вам за чтение здесь :)
Решение
Как отметил Edric, преобразования не рассматриваются во время вывода аргумента шаблона. Здесь у вас есть два контекста, где параметр Template T может быть выведен из типа аргументов:
template<class T>
v<T> operator+(V<T> const&, V<T> const&);
~~~~~~~~~~~ ~~~~~~~~~~~~
Но вы пытаетесь вызвать этот шаблон функции с V<float>
на левой стороне и с на правой стороне. Результаты удержания аргумента шаблона в T = float для левой стороны, и вы получите ошибку для правой стороны, потому что нет T, чтобы V<T>
равняться S<T>
. Отказ Это квалифицируется как сбой вывода аргумента шаблона, а шаблон просто игнорируется.
Если вы хотите разрешить преобразования, ваш оператор + не должен быть шаблоном. Есть следующий трюк: вы можете определить его как встроенный друг внутри шаблона класса для V:
template<class T>
class V
{
public:
V();
V(S<T> const&); // <-- note: no explicit keyword here
friend V<T> operator+(V<T> const& lhs, V<T> const& rhs) {
...
}
};
Таким образом, оператор больше не шаблон. Таким образом, нет необходимости в вычете аргумента шаблона, и ваш вызов должен работать. Оператор содержится через ADL (поиск в зависимости от аргументов), потому что левая сторона V<float>
. Отказ Правая сторона правильно преобразуется в V<float>
также.
Также возможно отключить вывод аргумента шаблона для определенного аргумента. Например:
template<class T>
struct id {typedef T type;};
template<class T>
T clip(
typename id<T>::type min,
T value,
typename id<T>::type max )
{
if (value<min) value=min;
if (value>max) value=max;
return value;
}
int main() {
double x = 3.14;
double y = clip(1,x,3); // works, T=double
}
Несмотря на то, что тип первого и последнего аргумента - это INT, они не рассматриваются во время вычета аргумента шаблона, потому что id<T>::type
не так называемый * выдумывающий контекст`. Таким образом, T выводится только в соответствии со вторым аргументом, который приводит к T = двойным без противоречий.
Другие советы
При рассмотрении шаблонов совпадает, неявные преобразования не используются. Следовательно, в следующем простом примере:
template < typename T >
void foo( T t1, T t2 ) { /* do stuff */ }
int main( int argc, char ** argv ) {
foo( 1, 1.0 );
return 0;
}
Это не будет компитен, даже если либо аргумент может быть неявно преобразован в другой тип (INT <-> Double).
Просто предположение, но, возможно, компилятор не может различать преобразование от V-> S или от S-> V, пытаясь выяснить, как добавить A + C в выражении 2. Вы предполагаете, что компилятор будет достаточно уменным Выберите тот, который позволяет компиляции действовать из-за остальных доступных функций, но компилятор, вероятно, не «читает впереди» (так сказать), и запутается с двусмысленностью преобразования UP, прежде чем пытаться найти Оператор '+'.
Конечно, если вы добавили ошибку компиляции, это может помочь уточнить проблему ...