Вопрос

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

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

Код у меня есть следующее:

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

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

Выражение 3.

  1. слияние преобразуется в 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. слияние преобразуется в 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! Какие государства:

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

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

У меня действительно нет подсказки ...

Ну, есть ли у вас ответ или нет, спасибо вам за чтение здесь :)

Это было полезно?

Решение

Как отметил 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, прежде чем пытаться найти Оператор '+'.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top