c++implicit-conversionexpression-templates

c++ Force implicit conversion on pass as argument


I have problem with implicit conversions in C++.

I'm trying to create some Expression template for vector arithmetics (I know that same libraries already exists. I'm just learning C++ so I wanted to try something with templates).

I would like to create class Vector, that is able to compute like this:

simd::test::Vector<char, 5> a;
simd::test::Vector<short, 5> b;

auto ret = a + b + a + b;

, where on output would be Vector of shorts becouse short is bigger type than char.

Right now, I have class that is able to adds vectors of same data types. For different types I have to call explicit conversion:

//simd::test::Vector<short, 5>(a)
auto ret = simd::test::Vector<short, 5>(a) + b + simd::test::Vector<short, 5>(a) + b;

Is possible to implicit convert Vector before pass into function "operator+()"? Here is my code of Vector:

#pragma once

#include <type_traits>

namespace simd {
  namespace test {

  template<typename R, std::size_t Dim,
      typename std::enable_if<std::is_arithmetic<R>::value>::type* = nullptr
  >
  class Vector_expression {
    public:
      static constexpr std::size_t size = Dim;

      virtual const R operator[] (std::size_t index) const = 0;

      virtual ~Vector_expression() = default;

  };

  template<typename T, std::size_t Dim>
  class Vector final : public Vector_expression<T, Dim> {
    private:
      T data[Dim];
    public:
      Vector() = default;

      template<typename R>
      Vector(const Vector_expression<R, Dim> &obj) {
        for(std::size_t index = 0; index < Dim; ++index) {
          data[index] = obj[index];
        }
      }

      template<typename R>
      Vector(Vector_expression<R, Dim> &&obj) {
        for(std::size_t index = 0; index < Dim; ++index) {
          data[index] = obj[index];
        }
      }

      template<typename R>
      Vector<T, Dim> & operator=(const Vector_expression<R, Dim> &obj) {
        for(std::size_t index = 0; index < Dim; ++index) {
          data[index] = obj[index];
        }
        return (*this);
      }

      template<typename R>
      Vector<T, Dim> & operator=(Vector_expression<R, Dim> && obj) {
        for(std::size_t index = 0; index < Dim; ++index) {
          data[index] = obj[index];
        }
        return (*this);
      }

      virtual const T operator[] (std::size_t index) const override {
        return data[index];
      }

      T & operator[] (std::size_t index) {
        return data[index];
      }

      virtual ~Vector() = default;
    };

    template<typename E1, typename E2, typename R, std::size_t Dim>
    class Vector_sum final : public Vector_expression<R, Dim> {
      private:
        const E1 & _lhs;
        const E2 & _rhs;
      public:
        Vector_sum() = delete;

        Vector_sum(const E1 & lhs, const E2 & rhs) :
            _lhs(lhs),
            _rhs(rhs)
        {}

        virtual const R operator[] (std::size_t index) const override {
          return _lhs[index] + _rhs[index];
        }

        virtual ~Vector_sum() = default;
    };

    template<typename R, std::size_t Dim>
    Vector_sum<Vector_expression<R, Dim>, Vector_expression<R, Dim>, R, Dim> operator+ (const Vector_expression<R, Dim> & lhs, const Vector_expression<R, Dim> & rhs) {
      return {lhs, rhs};
    }

  }
}

Solution

  • Just define an operator+ that allows different argument types. The one catch is determining the element type of the resulting sum. Probably the best option is to use whatever the result of adding two elements is. One way to write this type is:

    decltype(std::declval<const R1>() + std::declval<const R2>())
    

    Or if you know the types are built-in arithmetic types, that would be the same as

    std::common_type_t<R1, R2>
    

    Or using a trailing return type, we can take advantage of the function parameters to shorten the std::declval expressions:

    template<typename R1, typename R2, std::size_t Dim>
    auto operator+ (const Vector_expression<R1, Dim> & lhs,
                    const Vector_expression<R2, Dim> & rhs)
    -> Vector_sum<Vector_expression<R1, Dim>, Vector_expression<R2, Dim>,
                  decltype(lhs[0] + rhs[0]), Dim>
    {
      return {lhs, rhs};
    }