4

I want to do the following with specialization by traits.

  1. Array Aa = Scalar in_a would use overload I.

  2. Array Aa = Array Bb would use overload II.

In the following code, overload II never get used.

Someone mentioned that T1 cannot be deduced in overload II.

How to fix that?

I used the C++ shell to compile the code with C++14.

#include <iostream>
#include <type_traits>

using namespace std;
class A; // forward declaration.

template <typename T>
struct is_A : false_type {};
template <> struct is_A<A> : true_type {};

template <typename T>
struct is_int : false_type {};
template <> struct is_int<int> : true_type {};
template <> struct is_int<long> : true_type {};

class A{
    public:
        int val;
        void print(void){
            std::cout << val << std::endl;
        }
        template <typename T1>
        enable_if_t<is_int<T1>::value,void>
        operator=(const T1 & input){
            val = 2*input; //Overload I
        }
        template <typename T1>
        enable_if_t<is_A<T1>::value,void>
        operator=(const T1 & Bb){
            val = 5*Bb.val; //Overload II
        }
};

int main(void){
    A Aa;
    A Bb;
    int in_a = 3;
    Aa = in_a; //This uses overload I as intended.
    Bb = Aa; //I want this to use overload II, but
             //actually overload I is used.
             //This leads to an error during compilation.
    Aa.print(); //This should give 6. (3x2)
    Bb.print(); //This should give 30. (6x5)
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
rxu
  • 1,369
  • 1
  • 11
  • 29

3 Answers3

2

Your code should be

template <typename T>
std::enable_if_t<is_int<T>::value, A&>
operator=(const T& input){
    val = 2 * input; //Overload I
    return *this;
}
template <typename T>
std::enable_if_t<is_A<T>::value, A&>
operator=(T& rhs){
    val = 5 * rhs.val; //Overload II
    return *this;
}

Demo

But even simpler in your case

A& operator=(int input){
    val = 2 * input; //Overload I
    return *this;
}

A& operator=(const A& rhs){
    val = 5 * rhs.val; //Overload II
    return *this;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I definitely need the `enable_if_t` because that code is a simplified version of the actual code. Thanks a lot for the answer. I will test it on the actual code. – rxu Sep 14 '16 at 19:00
  • Here is the compilation result in c++ shell. http://cpp.sh/2xuum ... `error: 'enable_if_t' in namespace 'std' does not name a template type` – rxu Sep 14 '16 at 19:02
  • If compiling for c++14, running the compiled program gives `6 6` instead of `6 30`. http://cpp.sh/5jpn – rxu Sep 14 '16 at 19:07
  • copy assignment should not be template, you have to add `A& operator=(const A&)` – Jarod42 Sep 14 '16 at 19:23
  • Note that even if [the implicitly declared copy assignment operator for class `X` has `X&` as return type](http://eel.is/c++draft/class.copy#22), [it is not a requirement for an user-declared one](http://eel.is/c++draft/class.copy#19). Am I wrong? – skypjack Sep 14 '16 at 20:25
  • 1
    @skypjack: Indeed, it is not required, but it is good practice. As `operator ==` may return `std::string` instead of expected `bool`. – Jarod42 Sep 14 '16 at 20:35
  • @Jarod42 Once I was playing with this stuff about return types of operators and here on SO someone said me - _+1 for this is an example of standard-guerrilla _. I'm still laughing. – skypjack Sep 14 '16 at 20:49
  • I installed gcc 4.9.2. hopefully that will fit in with everything else... thanks again for the answer. – rxu Sep 15 '16 at 17:25
  • By the way, why would operator `==` return `std::string`? – rxu Sep 16 '16 at 15:33
  • I guess i sort of get this from http://stackoverflow.com/questions/5625790/template-assignment-operator-overloading-mystery and http://stackoverflow.com/questions/11706040/whats-the-difference-between-assignment-operator-and-copy-constructor – rxu Sep 16 '16 at 15:39
  • @rxu: I said that `std::string operator ==(/*..*/)` is valid but surprising. We expected `bool operator ==(/*..*/)`, in the same way as we don't expect `void print(std::string s)` to erase a file named `s`. There are conventions/guide-lines (and it is better to understand them), and we should have good reasons to not follow them. To be coherent with built-in as `int`, and allow syntax `a = (b = c);`, assignment should return something which is assignable to its self type, so `A& operator=(const A&)`. In the same way for equality: `bool operator == (const A&, const A&)`. – Jarod42 Sep 16 '16 at 17:44
2

Here is your code simplified and working as intended:

#include <iostream>
#include <type_traits>
#include<utility>

class A;

template <typename T>
struct is_A : std::false_type {};
template <> struct is_A<A> : std::true_type {};

template <typename T>
struct is_int : std::false_type {};
template <> struct is_int<int> : std::true_type {};
template <> struct is_int<long> : std::true_type {};

class A{
public:
    int val;

    void print(void){
        std::cout << val << std::endl;
    }

    template <typename T1>
    std::enable_if_t<is_int<std::decay_t<T1>>::value, void>
    operator=(T1 && input){
        val = 2*std::forward<T1>(input);
    }

    template <typename T1>
    std::enable_if_t<is_A<std::decay_t<T1>>::value,void>
    operator=(T1 && Bb){
        val = 5*std::forward<T1>(Bb).val;
    }
};

int main(void){
    A Aa;
    A Bb;
    int in_a = 3;
    Aa = in_a;
    Bb = Aa;
    Aa.print(); //This should give 6. (3x2)
    Bb.print(); //This should give 30. (6x5)
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • Thank you for the answer. i need to define my own traits and is_same won't work. I am posting another question for this. – rxu Sep 14 '16 at 19:50
  • @rxu Well, the answer still applies. Use your own traits and it will work as well. – skypjack Sep 14 '16 at 19:53
  • the actual case is that float, int, short etc will be considered scalar. matrix type A, matrix type B, matrix type C will be considered is_A... and i have an ancient standard library. i can only copy a few things from the newest standard library. – rxu Sep 14 '16 at 19:56
  • I guess i will comment what is simplified when i simplified code next time... my bad. – rxu Sep 14 '16 at 19:57
  • '*' looking for implementation of `decay`'*' – rxu Sep 14 '16 at 19:59
  • yup. that is how i copied std library. decay, and every dependencies of decay. – rxu Sep 14 '16 at 20:30
  • Note that you still have to write the `operator=(const A&)` to handle it as it is special. – Jarod42 Sep 14 '16 at 20:37
  • @Jarod42 Fair enough. It is left as an exercise to the reader. :-) – skypjack Sep 14 '16 at 20:46
1

Do you really need all the template magic for your simple case?

#include <iostream>

class A;

class A{
public:
    int val;

    void print(void){
        std::cout << val << std::endl;
    }

    void operator =(const A& in){ val = in.val*5; }
    void operator =(int in) { val = in*2; }
};


int main(void){
    A Aa;
    A Bb;
    Aa = 3;
    Bb = Aa;
    Aa.print(); //This should give 6. (3x2)
    Bb.print(); //This should give 30. (6x5)
    return 0;
}
Alex Chudinov
  • 654
  • 1
  • 6
  • 12
  • this is the simplified code. i need the templates and specialization by traits for the actual code. There are many types of arrays for int, float, double etc as well as many types of scalar. Need the template for math between them. – rxu Sep 14 '16 at 20:44
  • It could be. However, it is hard to understand whole problem from a peace of code. Do you mean math like vector summation, multiplication and etc? Like in matlab? – Alex Chudinov Sep 15 '16 at 09:05
  • yes. math as in the numpy library of python, and also in matlab. – rxu Sep 15 '16 at 17:25
  • It was interesting for me, cause I did a templates-based lib of a matrix math some time ago. If you will use templates then you can remove cycles. I mean if you use vector elementwise operation like summation of two vectors (Vector), for example, you do not need to do a cycle "for(int i = 0; i < N;++i)" inside. – Alex Chudinov Sep 16 '16 at 12:31