Michbeckable
Michbeckable

Reputation: 1891

c++ - Implement template operator non-friend

I have a simple generice arithmetic vector class and want to implement the * operator for scalar multiplication:

template<class Value_T, unsigned int N>
class VectorT
{
public:
    typedef Value_T value_type;
    typedef unsigned int size_type;

    size_type GetNumElements() const { return N; }      

    // class member
    // elements in the vector
    value_type elements[N];     

    // the number of elements
    static const size_type size = N;
};

// scalar multiplication
template<class Value_T, unsigned int N>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, Value_T s)        
{
    VectorT<Value_T,N> vTemp(v);
    for (unsigned int i = 0; i < v.GetNumElements(); i++)
    {
        vTemp.elements[i] *= s;
    }
    return vTemp;
}

Using it like this ...

typedef VectorT<double, 3> Vec3;

int main(int argc, char* argv[])
{
    Vec3 v;
    Vec3 v2 = v * 2; // multiply with scalar int
    return 0;
}

... gives compiler error C2782 (MSVC2012) that the template parameter Value_T for the * operator is ambiguous: int or double.

If I define the *operator within my class as friend function the error is gone and it works fine for scalar int or double. But actually there is no need here for friend declaration as the public interface of class VectorT is sufficient for the operator (In my real code I have the members privat and some public accessor methods).

I want the scalar multiplication work only for the Value_T type: For a VectorT<int, N> I want only integer values be given to the *operator. I further want the *operator being commutativ. That's why I don't implement it as a simple member function, where the lefthand operator is always of type VectorT.

How to implement * operator as non-member and non-friend here?

Upvotes: 2

Views: 220

Answers (3)

AnT stands with Russia
AnT stands with Russia

Reputation: 320757

The answers posted so far suggest giving an independent template parameter to the type of function parameter s. This is a viable approach, but not the only one.

Alternatively, a better solution might be to exclude your second argument from the template argument deduction process by intentionally placing it into non-deduced context

// scalar multiplication
template<class Value_T, unsigned int N>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, 
                                    typename VectorT<Value_T, N>::value_type s)        
{
  ...
}

That way the compiler will deduce the template arguments from the type of v parameter, but not from the type of s parameter. And s will still have the proper type, just like you wanted it to.

The above approach takes advantage of the fact that your class template already provides a convenient inner typename value_type. In a more general case one can achieve the same by using an identity template

template <typename T> struct identity
{
  typedef T type;
};

template<class Value_T, unsigned int N>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, 
                                    typename identity<Value_T>::type s)        
{
  ...
}

The C++ standard library decided not to include std::identity template, since its functionality is apparently covered by std::decay and std::remove_reference. So, by using standard templates you can implement the general solution as

template<class Value_T, unsigned int N>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, 
                                    typename std::decay<Value_T>::type s)        
{
  ...
}

The following article mentions this specific matter http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3766.html

Upvotes: 3

Dimitrios Bouzas
Dimitrios Bouzas

Reputation: 42929

First of, you could use std::array instead of implementing VectorT. Nevertheless, the problem you have is due to the fact that the integral literal 2 in the expression Vec3 v2 = v * 2; is of type int. Consequently, the compiler can't deduce to the overloaded operator* of yours, since as it is implemented it works only in the cases where your VectorT contains elements of the same type with the multiplier.

In order to overcome this, you could add an additional template argument to your overloaded operator* like the example below:

template<class Value_T1, class Value_T2, unsigned int N>
VectorT<Value_T1, N> operator*(const VectorT<Value_T1, N>& v, Value_T2 s)        
{
    VectorT<Value_T1, N> vTemp(v);
    for(unsigned int i(0), sz(v.GetNumElements()); i < sz; ++i) {
        vTemp.elements[i] *= s;
    }
    return vTemp;
}

LIVE DEMO

Upvotes: 2

Matthias
Matthias

Reputation: 3566

Look at the operator's definition

const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, Value_T s) 

and on the line

Vec3 v2 = v * 2; // multiply with scalar int

operator* is called with parameters of type VectorT and int. So Value_T is double once and int the other time. You could multiply with 2.0 to resolve the ambiguity or add a third template parameter to the operator* definition:

template<class Value_T, unsigned int N, class ScalarType>
const VectorT<Value_T, N> operator*(const VectorT<Value_T, N>& v, ScalarType s)

Upvotes: 4

Related Questions