Неявное преобразование не происходит - PullRequest
7 голосов
/ 08 октября 2010

Последний вопрос, который я задал, был о чем-то, на что я наткнулся, пытаясь понять другую вещь ... которую я тоже не могу понять (не мой день).

Это довольно длинное утверждение вопроса, нопо крайней мере, я надеюсь, что этот вопрос может оказаться полезным для многих людей, и не только для меня.

У меня есть следующий код:

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

  1. c is неявно преобразуется в const с помощью стандартной последовательности преобразования (состоящей только из одного квалификационного преобразования ).
  2. V<float>(const S<T> & s) вызывается и временный объект const V<float> генерируется (давайте назовем это т ).Он уже квалифицирован как const, поскольку является временным значением.
  3. a преобразуется в const аналогично c .
  4. operator+(const V<float> & a, const V<float> & b) isвызывают, что приводит к временному типу const V<float>, который мы можем назвать q .
  5. , вызывается значение по умолчанию V<float>::operator=(const & V<float>).

Я в порядке?сюда?Если я допустил даже самую незначительную ошибку, пожалуйста, дайте мне знать, потому что я пытаюсь понять как можно глубже неявное приведение ...

Выражение 3

  1. c преобразуется в 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. вызывается значение по умолчанию V<float>::operator=(const & V<float>).

Выражение 2?

Я не понимаю, почему существует проблема со вторым выражением.Почему следующая последовательность невозможна?

  1. c преобразуется в 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. Шаги со 2 по 6 такие же, как в случае выражения 1.

После прочтения стандарта C ++ я подумал: «Эй!возможно, проблема связана с 13.3.3.1.2.3! 'который гласит:

Если пользовательское преобразование определено функцией преобразования шаблона, вторая стандартная последовательность преобразования должна иметь ранг точного соответствия.

Но это не может бытьслучай, поскольку квалификационное преобразование имеет точный ранг соответствия ...

Я действительно понятия не имею ...

Хорошо, есть ли у вас ответ или нет, спасибо, что прочитали здесь:)

Ответы [ 3 ]

12 голосов
/ 08 октября 2010

Как указал Эдрик, преобразования не учитываются при выводе аргументов шаблона. Здесь у вас есть два контекста, где параметр шаблона T может быть выведен из типа аргументов:

template<class T>
v<T> operator+(V<T> const&, V<T> const&);
               ~~~~~~~~~~~  ~~~~~~~~~~~~

Но вы пытаетесь вызвать этот шаблон функции с V<float> с левой стороны и с S с правой стороны. Вывод аргумента шаблона приводит к 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
}

Несмотря на то, что тип первого и последнего аргумента является целым, они не учитываются при выводе аргумента шаблона, поскольку id<T>::type не является так называемым * выводимым контекстом`. Таким образом, T выводится только согласно второму аргументу, что приводит к T = double без противоречий.

4 голосов
/ 08 октября 2010

При рассмотрении совпадений с шаблонами неявные преобразования не используются.Поэтому в следующем простом примере:

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

0 голосов
/ 08 октября 2010

Просто предположение, но, возможно, компилятор не сможет отличить преобразование от V-> S или от S-> V, пытаясь выяснить, как добавить + c в выражение 2. Вы предполагаете, что компилятор будет достаточно умен, чтобы выбрать тот, который позволяет продолжить компиляцию из-за остальных доступных функций, но компилятор, вероятно, не «читает вперед» (так сказать) и запутывается с неоднозначностью преобразования вверх перед пытаясь найти оператор «+».

Конечно, если вы добавили ошибку компиляции, это также может помочь решить проблему ...

...