У меня есть класс шаблонов, для которого я перегружаю operator+
, но вам нужно, чтобы он потенциально возвращал другой тип данных, чем тот, для которого operator+
экземпляр класса. Например, следующий фрагмент выполняет стандартное математическое определение векторного векторного вектора (скалярного произведения), векторного скалярного или скалярного * вектора. Чтобы быть конкретным, допустим, что у меня есть Vector<int>
а скаляр типа double
- я хочу вернуть double из функции "векторный скаляр" operator+
.
Я уверен, что мне не хватает инструкций template<class T>
для функций friend (?), Но этот фрагмент не предназначен для компиляции.
template<class T>
class Vector
{
private:
std::vector<T> base;
public:
friend Vector operator*(const Vector& lhs, const Vector& rhs); // inner product
Vector<T> operator*(const T scalar); // vector*scalar
friend Vector operator*(const T scalar, const Vector& rhs); // scalar*vector
};
template<class T>
Vector<T> operator*(const Vector<T>& lhs, const Vector<T>& rhs) // inner product
{
assert( lhs.base.size() == rhs.base.size() );
Vector result;
result.base.reserve(lhs.base.size());
std::transform( lhs.base.begin(), lhs.base.end(), rhs.base.begin(), std::back_inserter(result.base), std::multiplies<T>() );
return result;
}
template<class T>
Vector<T> Vector<T>::operator*(const T scalar) // vector*scalar
{
Vector result;
result.base.reserve(base.size());
std::transform( base.begin(), base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );
return result;
}
template<class T>
Vector<T> operator*(const T scalar, const Vector<T>& rhs) // scalar*vector
{
Vector result;
result.base.reserve(rhs.base.size());
std::transform( rhs.base.begin(), rhs.base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );
return result;
}
Я предполагаю, что вы хотите вернуть вектор с типом значения, являющимся типом, возвращаемым операцией для каждого компонента, что не обязательно является типом скаляра.
Например:
Vector<int> * int -> Vector<int>
Vector<int> * double -> Vector<double>
Vector<double> * int -> Vector<double>
Vector<char> * float -> Vector<float>
и т.п.
Для этого вы должны определить два типа ввода отдельно, допустим, T1
и T2
(и один или два из операндов являются его Vector
). Вы не хотите просто использовать скалярный тип (для векторного * скалярного или скалярного векторного действия) в качестве результата, в противном случае он может быть преобразован (см. Мой третий пример: результатом будет Vector<int>
.
Вышеприведенное может быть выполнено с помощью decltype
для поиска третьего (результата) типа.
Для простоты определите оператор как не-член:
template<typename T1, typename T2, typename T3 = decltype(std::declval<T1>() * std::declval<T2>())>
Vector<T3> operator*(const Vector<T1>& lhs, const T2 & scalar) // inner product
{
Vector<T3> result;
//...
return result;
}
Интересная часть
T3 = decltype(std::declval<T1>() * std::declval<T2>())
Здесь мы находим тип T3
используя два других типа T1
и T2
. Во-первых, мы строим два значения с несущественным значением (функция std::declval
является вспомогательной функцией, возвращающей тип, заданный как параметр шаблона). Затем мы умножаем эти значения, но опять же результат неважен; нас интересует только тип. Это то, что делает третья часть: decltype
дает вам тип выражения (без его оценки).
Другие операторы могут быть реализованы аналогично.
Чтобы сделать этих операторов друзьями, вам нужен синтаксис
template<...> friend ...
как видно из ответа Данвилла.
operator+
я никогда не объявляю тип для Vector result
, например, я не писал Vector<T> result
, но исходный код, который он получил, работает. Тип присваивается неявно?
Декларация друга должна выглядеть так:
template<class S>
class Vector
{
private:
std::vector<S> base;
public:
template<class T> friend T operator*(const Vector<T>& lhs, const Vector<T>& rhs); // inner product
template<class T> friend Vector<T> operator*(const T scalar); // vector*scalar
template<class T> friend Vector<T> operator*(const T scalar, const Vector<T>& rhs); // scalar*vector
};
Остальная часть кода выглядит так:
template<class T>
T operator*(const Vector<T>& lhs, const Vector<T>& rhs) // inner product
{
assert( lhs.base.size() == rhs.base.size() );
Vector<T> result;
result.base.reserve(lhs.base.size());
std::transform( lhs.base.begin(), lhs.base.end(), rhs.base.begin(), std::back_inserter(result.base), std::multiplies<T>() );
return result;
}
template<class T>
Vector<T> operator*(const Vector<T>& lhs, const T scalar) // vector*scalar
{
return scalar*lhs;
}
template<class T>
Vector<T> operator*(const T scalar, const Vector<T>& rhs) // scalar*vector
{
Vector<T> result;
result.base.reserve(rhs.base.size());
std::transform( rhs.base.begin(), rhs.base.end(), std::back_inserter(result.base), std::bind1st(std::multiplies<T>(), scalar) );
return result;
}
Вы можете добавить другие параметры шаблона:
template<class T, class S>
Vector<S> Vector<T>::operator*(const S scalar)
и убедитесь, что вы используете S
для скалярного типа и T
для типа векторного элемента в правильных местах в теле функции.
int * double
, в этом примереdouble
вместоint
. Это возможно сdecltype
.