Перегрузка оператора для автоматического преобразования типов в шаблонном классе

0

У меня есть класс шаблонов, для которого я перегружаю 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;
}
  • 0
    Что такое сообщение об ошибке компилятора?
  • 1
    Я предполагаю, что вы хотите вернуть Vector с типом значения, являющимся типом, возвращаемым операцией для каждого компонента, то есть, что приводит к int * double , в этом примере double вместо int . Это возможно с decltype .
Теги:
templates
vector

3 ответа

1
Лучший ответ

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

Например:

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

как видно из ответа Данвилла.

  • 0
    Спасибо! Считается ли хорошей практикой объявлять подобные вещи встроенными? (Я думаю, что это то, что у вас есть, в любом случае)
  • 0
    Кроме того, я заметил в своем коде, что внутри определения operator+ я никогда не объявляю тип для Vector result , например, я не писал Vector<T> result , но исходный код, который он получил, работает. Тип присваивается неявно?
Показать ещё 9 комментариев
1

Декларация друга должна выглядеть так:

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;
}
  • 0
    Благодарю. Есть ли польза от объявления второго «вектора * скаляра» другом, даже если он будет вызван для объекта класса?
  • 0
    Просто для симметрии :)
0

Вы можете добавить другие параметры шаблона:

template<class T, class S>
Vector<S> Vector<T>::operator*(const S scalar)

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

  • 0
    Спасибо, но есть ли способ сделать это так, чтобы результат всегда был «выше» двух типов данных, будь то вектор или скаляр? Кажется, что это только вернет тип данных, которым является скалярный объект, но что если вектор был бы двойным, а скаляр - целым?
  • 0
    @ Давид Да, смотрите мой ответ.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню