Как указал Эдрик, преобразования не учитываются при выводе аргументов шаблона. Здесь у вас есть два контекста, где параметр шаблона 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 без противоречий.