0

I am using template class type datatypes as arguments for a method of a class. In that method, I am calculating the difference of the arguments and printing it. This is just a sample code from the actual project.

Header.h

#ifndef Header_h
#define Header_h

#include <iostream>
#include "string.h"

template <class T>
class Student
{
public:
    Student(T);
    void calcDifference(int idx, T val1, T val2);

};
#endif

main.cpp

#include <iostream>
#include "Header.h"

using namespace std;

template <class T>
void Student<T>::calcDifference(int idx, T val1, T val2)
{
    T difference  = val1 - val2;
    cout<<"\nDifference values: "<<difference<<endl;

}

template <class T>
Student<T>::Student(T)
{
    cout<<"constructor  called";
}


int main(int argc, const char * argv[])
{

    Student<int> oStudent(10);
    oStudent.calcDifference(1, 12, 10);


    //THIS FOLLOWING OBJECT CREATES ERROR BECAUSE IT IS TRYING TO PASS STRING TYPE
    Student<string> o2_Student("hi");
    o2_Student.calcDifference(1, "aaa", "aa");

    return 0;
}

Problem: As, I am trying to calculate the difference between the arguments inside the calcDifference(), the second object in the main() which is trying to pass strings creates problem. It is because the difference operation cannot be performed at strings (at least directly).

Error: /Users/siddharth/coding/cplusplus/xCode/CPPCodes/InterviewTest/InterviewTest/main.cpp:9:26: Invalid operands to binary expression ('std::__1::basic_string<char>' and 'std::__1::basic_string<char>')

What I need: I want that the code remain generic (for the calling function which I can't change acually). I want to find some solution so that I won't get this complier error and if the arguments passed are of string type then, the calcDifference() should print the an statement (like "String datatype is not allowed") and return to the calling function. I can make changes inside the calcDifference() ONLY because in the actual project I do not have control at the calling function. I think that exception-handling cannot help in this case because it is helpful to catch runtime error but in this case, I am getting compile time error as the calling function is trying to pass string (template T is made string).

PS: I cannot make any change in this structure which means that I cannot even change the number of arguments etc. I can only make changes inside the method calcDifference() .

skm
  • 5,015
  • 8
  • 43
  • 104
  • Can you add an extra parameter to `calcDifference` which tells it how to handle the two objects? It can default to `std::less` but allow you to provide your own function. – Neil Kirk Sep 26 '15 at 21:48
  • No, I cannot make any change in this structure. I can only make changes inside the method. – skm Sep 26 '15 at 21:49
  • 1
    How would you like to "*handle this case*"? what are you expecting to happen? (You don't want a compile time error, right?) – Amit Sep 26 '15 at 21:50
  • Inside `calcDifference` you could call a free helper function to compute the difference and overload it appropriately. – Alan Stokes Sep 26 '15 at 21:51
  • May be, I did not find the suitable title. I just meant, how do I deal with a situation when the calling function is trying to pass strings. – skm Sep 26 '15 at 21:52
  • Indeed, the code makes no sense. So why are you writing it? What is your goal? This makes no sense. Are you just trying to prevent users of your library from attempting to calculate the difference of strings? Well, you've accomplished that already! This compiler error will prevent them from doing so. What is the problem? – Lightness Races in Orbit Sep 26 '15 at 21:55
  • @LightnessRacesinOrbit: The compiler should not throw any error. Rather this case should also be possible and then, I will pass the comment from `calcDifference()` function that string arguments have been passed which are invalid datatypes for this program. – skm Sep 26 '15 at 22:00
  • @skm: What does the "difference between two strings" even mean? This makes no sense. – Lightness Races in Orbit Sep 26 '15 at 22:00
  • @LightnessRacesinOrbit: I know that it does not make any sense that's why I want to handle such situation. As, I have already mentioned that this sample code is a MWE for an actual project. – skm Sep 26 '15 at 22:02
  • @skm: "Handle" it how??!?! What does that mean for you? You have not told us. In my mind, the only sensible thing to do is to prevent the operation, which is exactly what is already occurring. If you have some other "handling" in mind, you need to _tell us what that is_. As it stands, your question is exceptionally vague; you're basically just saying "I want to do a thing" without telling us what thing you want to do. As such, and even if R Sahu won't admit it, all answers will be guesses. – Lightness Races in Orbit Sep 26 '15 at 22:03
  • @skm How do you want _"handle this case"_? – Nevermore Sep 26 '15 at 22:38
  • "handle this case": I just want that the `calcDifference()` print a simple message "String datatype is not allowed" and return to the calling function for further tasks. – skm Sep 26 '15 at 22:59
  • Make `calcDifference` call an external function which itself is specialized for strings. – Neil Kirk Sep 26 '15 at 23:33
  • @NeilKirk: I have already mentioned that I cannot make any change in the structure. The only things in my hand is the body of the `calcDifference()`. I do not know if this is possible or not and that's why I am taking the inputs of other people at SO. – skm Sep 27 '15 at 07:31
  • My suggestion does not require you to change the structure., only to call a new function from inside it. – Neil Kirk Sep 27 '15 at 20:01
  • @NeilKirk: Sorry I did not understand your suggestion. Kindly elaborate a little more. – skm Sep 27 '15 at 21:31
  • Can you call a function inside `calcDifference` which is defined outside the class? – Neil Kirk Sep 27 '15 at 22:27
  • @NeilKirk: No, that is not possible. I have make changes inside those parentheses only. Can't lambda function of C++11 help in this case which can be defined inside a function (I am not sure, haven't used)? – skm Sep 28 '15 at 07:39

2 Answers2

1

The line

T difference  = val1 - val2;

is a problem when val1 - val2 is not defined for a type. That is the case when T = std::string. That's what the compiler is complaining about.

One way to resolve the problem is to use a helper struct template that will work for most types using a generic logic but will allow you to specialize it for types for which val1 - val2 is not defined.

Define a helper struct

template <typename T> struct difference
{
    T operator()(T val1, T val2) const { return val1 - val2; }
};

Instead of using

T difference  = val1 - val2;

use

T diff  = difference<T>()(val1, val2);

Specialize difference for std::string.

template <> struct difference<std::string>
{
    std::string operator()(std::string const& val1,
                           std::string const& val2) const
    {
       // Figure what it means to compute the difference
       // between two strings for your application.
        return stringDifference(val1, val2);
    }
};
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

I think there is a very easy solution: Template specialization.

Just add the following specialization of your difference method for T beeing string:

template <>
void Student<string>::calcDifference(
    int idx, string const & val1, string const & val2)
{
    cout << "Computing string difference (" << val1 << " - " << val2 
         << ") is undefined!" << endl;
}

Whenever you call calcDifference on strings, the specialized version of the implementation is preferred and hence used. I've completed your example here. This way, you don't need to change any calling function.

Further note: I've added some const & for dealing with template parameters. It's good practice in general, but essential for templates: Some passed T may be huge and hence costly to copy just for computing the difference.

m8mble
  • 1,513
  • 1
  • 22
  • 30
  • thanks, but as I have already mentioned that I want to make changes only inside the body of `calcDifference()` so, this solution will not help me (I mean, it's fine but I am looking for a way so that I don't need to add any other method/function). – skm Sep 27 '15 at 09:00