Решение уже показано в этот ответ . Теперь подробнее о проблеме ...
Проблема в вашем коде заключается в том, как выполняется разрешение перегрузки. Когда функция шаблона рассматривается для разрешения перегрузки, компилятор выполнит дедукцию типа для аргументов и придет к подстановке типа, которая соответствует вызову, иначе он не сможет применить этот шаблон, удалит его из набора потенциальных кандидатов и продолжит снова. Проблема в этой точке состоит в том, что вывод типа только выводит точные соответствия (с возможной дополнительной константой / изменчивой квалификацией). Поскольку сопоставление является точным, компилятор не будет использовать никакого преобразования (опять же, кроме cv).
Простейший пример этого происходит с функциями std::max
и std::min
:
unsigned int i = 0;
std::min( i, 10 ); // Error!
Вывод типа выведет T в template <typename T> min( T const &, T const & )
для unsigned
для первого аргумента, но для int
для второго они различаются, и компилятор откажется от этой функции шаблона.
Решение, предложенное в ответе , использует функцию языка, которая позволяет вам определять функцию, не являющуюся членом-другом, в определении класса. Преимущество шаблонов заключается в том, что для каждого (различного) создания шаблона компилятор создает свободную функцию без шаблона на уровне пространства имен, в которой есть сигнатура, полученная путем подстановки реальных типов создания в объявлении друга:
template <typename T>
class test {
friend test operator+( test const & lhs, test const & rhs ) { // [1]
return test();
}
}
test<int> t; // [2]
В приведенном выше примере компилятор позволяет вам добавить определение функции-друга в область видимости класса в [1]. Затем, когда вы создадите экземпляр шаблона в [2], компилятор сгенерирует свободную функцию:
test<int> operator+( test<int> const & lhs, test<int> const & rhs ) {
return test<int>();
}
Функция определяется всегда , независимо от того, используете вы ее или нет (это отличается от функций-членов класса шаблона, которые создаются по требованию).
Магия здесь имеет несколько сторон. Первая часть заключается в том, что generics вы определяете не шаблонные функции для каждого и всех созданных экземпляров типов, так что вы получаете универсальность и в то же время преимущество разрешения перегрузки, позволяющее использовать эту функцию, когда аргументы не идеально совпадают.
Поскольку это не шаблонная функция, компилятор может вызывать неявные преобразования для обоих аргументов, и вы получите ожидаемое поведение.
Кроме того, при поиске продолжается другой тип magic , так как определенная таким образом функция может быть найдена только при зависимом от аргумента поиске , если она также не объявлена на уровне пространства имен, что в нашем случае это нельзя сделать общим способом. Смысл этого может быть хорошим или плохим, в зависимости от того, как вы хотите это рассмотреть ...
Поскольку он может быть найден только ADL, он не будет рассматриваться, если хотя бы один из аргументов уже имеет требуемый тип (т. Е. Он никогда не будет использоваться при выполнении преобразования в оба аргумента). Недостатком является то, что невозможно обратиться к функции, если вы на самом деле не вызываете ее, а это означает, что вы не можете получить указатель на функцию.
(Подробнее о шаблоне дружбы здесь , но обратите внимание, что в этом конкретном случае все остальные варианты не смогут выполнять неявные преобразования).