0

I have been trying to solve this bug for days. I made a generic array class, from which I used generic inheritance to create a numeric array. Everything works perfect; however, I am having trouble with multiplication/scaling of the numeric array. I overloaded the operator* in the following standard way:

  //Scaling operator 
   template <typename Type>
   NumericArray<Type>& NumericArray<Type> :: operator * (const double factor) // scaling the elements 
{
    for (int i = 0; i < (*this).size(); i++)
    {
        (*this)[i] *= factor; 
    }
    return *this; 
}

Of course, due to the inherent order demanded by this implementation, I can only do multiplication in the way of array * 2. But I also want to be able to write 2 * array. So I decided to implement a friend function as follows:

template <typename Type> 
NumericArray<Type> operator* (Type factor, const NumericArray<Type>& num_array)
{
    return(num_array * factor);
}

The declaration of this friend function is as follows:

template <typename Type> friend NumericArray<Type> operator * (double factor, const NumericArray<Type>& num_array);

But when I try to write 2 * array in main(), I get the error message:

Severity Code Description Project File Line Error C2678 binary '*': no operator found which takes a left-hand operand of type 'const NumericArray' (or there is no acceptable conversion)

The error makes me think that main() doesn't even recognize that the friend function exists. I have used friend functions a few times before, but I am pretty new to templates. I've been told that templates and generic programming in general has some weird quirky necessities, which may cause unforeseen circumstances.

For anyone who wants to see the full program, see the dropbox link: https://www.dropbox.com/sh/86c5o702vkjyrwx/AAB-Pnpl_jPR_GT4qiPYb8LTa?dl=0

Thanks again for your help:)

Ryan J. Shrott
  • 592
  • 1
  • 8
  • 26

3 Answers3

1
NumericArray<Type>& operator * (const double factor)

This is a non const member function. This means it may mutate the object it's called on.

Your parameter in the free operator function (which doesn't need to be a friend if calling the public operator above) is

const NumericArray<Type>&

and therefore const qualified.

To solve it, you need to change the member function to be callable on const qualified instances (given that it really does not mutate it's instance, which it shouldn't)(*):

NumericArray<Type>& operator * (const double factor) const

(*) Yours is mutating it's instance, this is counter intuitive:

c = a * b;

You don't want a to be changed after that, in general.


As Nicky C suggests in his answer, the best approach here is probably to have an operator*= as (non const, public) member function, which is doing what currently your operator* seems to be doing.

Then you add two free operator* functions which use the operator*= to implement their behaviour.

So no need for friends in this case.

Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
1

I am not sure whether I want to explain everything since it can be complicated if we go deep. But in short:

  1. You've mixed up the meanings and implementations of *= and *.
  2. The errors involve a lot of things: template instantiation, const-correctness, overload resolution...

So, let's just look at the idiomatic way to do so.

Step 1: Write a member operator *=.

template <typename Type>
class NumericArray
{
    //...

    NumericArray& operator *= (const Type factor)
    {
        [[ Code that scales up every element, i.e. your for-loop ]]

        return *this;
    }

    //...
}

Step 2: Write a pair of non-member non-friend operator *

template <typename Type>
NumericArray<Type> operator * (const NumericArray<Type>& num_array, const Type factor)
{
    NumericArray<Type> new_num_array = num_array; // Copy construct
    new_num_array *= factor; // Scale up
    return new_num_array;
}

// And similarly for factor*num_array
  • The member function is no template. It's an definition outside of the class definition that he's showing, to correctly reference the associated class you need the template specification of that class. – Daniel Jour Jul 09 '15 at 06:10
  • @DanielJour These changes make my code run with no errors. But when I test the implementation in main(), I get rubbish values as results (for example the result is an array filled with all -842150451). – Ryan J. Shrott Jul 09 '15 at 06:44
  • Do you have a working copy constructor? (Default or own implementation) – Daniel Jour Jul 09 '15 at 06:52
  • @DanielJour I created my own constructors – Ryan J. Shrott Jul 09 '15 at 07:00
  • But did you test them? Nicky C's code relies upon their correct behaviour. – Daniel Jour Jul 09 '15 at 07:02
  • @DanielJour I just deleted my copy constructor implementations and relied on the default instead, and now everything works. Before, my custom copy constructor simply called the copy constructor of the generic Array class with Array::Array(source) in the body. – Ryan J. Shrott Jul 09 '15 at 07:18
  • here is a dropbox to the program: https://www.dropbox.com/sh/86c5o702vkjyrwx/AAB-Pnpl_jPR_GT4qiPYb8LTa?dl=0 – Ryan J. Shrott Jul 09 '15 at 08:12
  • @Ryan In general, prefer compiler-generated copy/move constructor/assignment whenever possible. In your code, the copy constructor is not copying into the member array, but creating a temporary local array. See http://en.cppreference.com/w/cpp/language/copy_constructor to know how. –  Jul 09 '15 at 10:14
1

NumericArray<Type> in the friend function is const, it can not call a not const function--- NumericArray<Type>& NumericArray<Type> :: operator * (const double factor)

kiviak
  • 1,083
  • 9
  • 10