Compiler selecting template function in error?

class A
{
...
A operator =(A &rhs);
A template<typename T> operator =(const T &rhs);
A operator -(A &rhs);
}


int main(void)
{
A number;
A otherNumber;
A result;

number="123"; //calls templated operator = as expected
otherNumber="321"; //calls templated operator = as expected
result=number - otherNumber; //calls templated operator = in error
}

I don't not understand why the templated version of the assignment operator is being called when minus operator returns an object of type A. The last line should resolve to:
A = A
After the call to the minus operator which should have the compiler call the non-templated version of the assignment operator yet the compiler selects the templated version and causes my program to error out.

Can someone explain to me why this is happening

Also I've tried to resolve the issue by creating an explicit template specialization for the assignment operator but VS2005 gives me errors.
I tried:
template <> operator =<>(A &rhs);
and
template <> operator =<A>(A &rhs);

I got errors for unexpected tokens etc. despite not being able to see syntax errors in the declarations. Is the syntax wrong Does VS not support specialization of operators Does C++ allow explicit template specializations of operators

Thankyou to anyone who gives up their time to help me.



Answer this question

Compiler selecting template function in error?

  • Marcos B

    It would be quicker for us to analyse this issue if we didn't have to write code. Could you provide a real example Thanks.
  • SteveTaylorMST

    #ifndef _MYINTEGER_H_
    #define _MYINTEGER_H_

    #include "smartpointer.h"
    #include <iostream>
    #include <sstream>

    using std::ostream;
    using std::istream;
    using std::stringstream;
    using std::cerr;
    using std::endl;

    class MyInteger
    {
    public:
    MyInteger();
    MyInteger(const char *integer);
    MyInteger(unsigned int &sz);
    MyInteger(MyInteger &myInt);
    ~MyInteger();
    ...
    ...
    MyInteger operator =(MyInteger &rhs);
    template<typename T>
    MyInteger operator =(const T &rhs);
    MyInteger operator -(MyInteger &rhs);
    template<typename T>
    MyInteger operator -(const T &rhs);
    ...
    ...
    friend ostream& operator <<(ostream &output,const MyInteger &rhs);
    friend istream& operator >>(istream &input,MyInteger &rhs);
    private:
    template<typename T>
    unsigned int calcSize(const T &str);
    template<>
    unsigned int calcSize<smartpointer<char>>(const smartpointer<char> &str);
    template<typename T>
    void convertToMyInter(const T &unvalidated,MyInteger *validated);
    void addSign(MyInteger &results);
    void clearZeros(MyInteger &results);
    void subtract(MyInteger &results,MyInteger lhs,const MyInteger &rhs);
    ...
    ...
    void reset();
    unsigned int size;
    smartpointer<char> digits;
    };

    #include "MyInteger.inl"

    #endif

    ------------------------------------------
    //////////////////////from MyInteger.inl

    template<typename T> void MyInteger::convertToMyInter(const T &unvalidated,MyInteger *validated)
    {
    //local variables
    char someChar;
    unsigned int sentry;
    bool signedFlag=false;
    stringstream sStream;
    int numDecimalPoints=i_zero;

    validated->size=calcSize(unvalidated)+i_one;
    validated->digits=new char[validated->size];
    sStream<<unvalidated;
    for(unsigned int i=i_zero;i<validated->size-i_one;++i)
    {
    someChar=sStream.get();
    if(i==0)
    {
    if(someChar=='-')
    {
    validated->digitsIdea=someChar;
    signedFlag=true;
    }
    else if(someChar>'9' && someChar<'0')
    {
    cerr<<"Fatal Error! "<<unvalidated<<" is not a valid integer. Program terminated."<<endl;
    exit(-1);
    }
    else
    {
    validated->digitsIdea=someChar;
    }
    }
    else if(someChar=='.')
    {
    (signedFlag) sentry=i_one : sentry=i_zero;
    if(!numDecimalPoints && i>sentry)
    {
    validated->digitsIdea=someChar;
    ++numDecimalPoints;
    }
    else
    {
    cerr<<"Fatal Error! "<<unvalidated<<" is not a valid integer. Program terminated."<<endl;
    exit(-1);
    }
    }
    else if(!numDecimalPoints)
    {
    if(someChar>'9' || someChar<'0')
    {
    cerr<<"Fatal Error! "<<unvalidated<<" is not a valid integer. Program terminated."<<endl;
    exit(-1);
    }
    else
    {
    validated->digitsIdea=someChar;
    }
    }
    else
    {
    if(someChar != '0')
    {
    cerr<<"Fatal Error! "<<unvalidated<<" is not a valid integer. Program terminated."<<endl;
    exit(-1);
    }
    else
    {
    validated->digitsIdea=someChar;
    }
    }
    }
    validated->digits[validated->size-i_one]='\0';
    }

    template<typename T> MyInteger MyInteger::operator =(const T &rhs)
    {
    convertToMyInter(rhs,this);
    return *this;
    }

    //////////////////////////from MyInteger.cpp

    MyInteger MyInteger::operator =(MyInteger &rhs)
    {
    if(*this!=rhs)
    {
    this->size=rhs.size;
    this->digits=rhs.digits;
    }
    return *this;
    }

    MyInteger MyInteger::operator -(MyInteger &rhs)
    {
    //local variables
    bool negLhs;
    bool negRhs;
    MyInteger temp;

    (this->digits[i_zero]=='-') negLhs=true : negLhs=false;
    (rhs.digits[i_zero]=='-') negRhs=true : negRhs=false;

    if(this->size > rhs.size)
    {
    temp.digits=new char[this->size];
    temp.size=this->size;
    subtract(temp,*this,rhs);

    if(negLhs)
    {
    addSign(temp);
    clearZeros(temp);
    return temp;
    }
    else
    {
    clearZeros(temp);
    return temp;
    }
    }
    else if(rhs.size > this->size)
    {
    temp.digits=new char[rhs.size];
    temp.size=this->size;
    subtract(temp,rhs,*this);

    if(negLhs)
    {
    addSign(temp);
    clearZeros(temp);
    return temp;
    }
    else
    {
    clearZeros(temp);
    return temp;
    }
    }
    else
    {
    //get significant digit info
    //the one with the significant sign wins
    for(unsigned int i=i_zero;i<this->size;++i)
    {
    if(this->digitsIdea>rhs.digitsIdea)
    {
    temp.digits=new char[this->size];
    temp.size=this->size;
    subtract(temp,*this,rhs);

    if(negLhs)
    {
    addSign(temp);
    clearZeros(temp);
    return temp;
    }
    else
    {
    clearZeros(temp);
    return temp;
    }
    break;
    }
    else if(rhs.digitsIdea>this->digitsIdea)
    {
    temp.digits=new char[rhs.size];
    temp.size=this->size;
    subtract(temp,rhs,*this);

    if(negLhs)
    {
    addSign(temp);
    clearZeros(temp);
    return temp;
    }
    else
    {
    clearZeros(temp);
    return temp;
    }
    break;
    }
    }
    temp.digits=new char[2];
    temp.digits[i_zero]='0';
    temp.digits[i_one]='1';
    return temp;
    }
    }

    -------------------------------------------
    #include "smartpointer.h"
    #include "digits.h"
    #include "MyInteger.h"
    #include <vector>

    using namespace std;

    int main(void)
    {
    MyInteger someInt();
    MyInteger dummy;

    someInt="321";
    dummy="123";
    someInt=(dummy-someInt); //errors out here
    cout<<someInt<<endl;
    cout<<someInt[someInt.getSize()]<<endl;

    return i_zero;
    }
    ----------------------------------------------------

    I need to handle MyInteger's differently when assigning values from one to another.
    I use a template to assign values stored in integral types to MyInteger.

    The member convertToMyInteger checks that values passed into the template function are valid integers.

    If one where to pass in -1 to convertToMyInteger it would accept the value as the assignment would complete.
    If one where to pass in a MyInteger storing -1 to convertToMyInteger the function will error out due to how values are internally validated. However, that is the reason there is overload of the assignment operator that handles objects of type MyInteger. It would make much sense for an MyInteger instance to not be an integer.

    My problem lies with the template function being selected by the compiler in preferrence to the non-template assignment operator overload.

    I don't understand why this is happening or how to prevent it from happening. I will appreciate any help anyone can give me.


  • Tiarnan

    Holger Grund wrote:

    I'm not exactly certain what you're trying to achieve, but obviously operator- will return a rvalue and a non-const reference can't bind to a temporary. I don't think there's anything special in this case that would require selection of the copy-assignment operator. Either way, it would be ill-formed at best.

    So you might want to define a copy-assignment operator with a const argument.

    The second specialization looks good, but you're missing the const. You could also let the compiler deduce arguments which requires you to omit the angle brackets.

    -hg



    I didn't want to use a deep copy because I was using a reference counted smart pointer. However, when I think about it I don't want operations with one instance to affect another so I would seem a deep copy is the only thing that makes sense after all.

    Thanks. I'll change that.

    I'll add the const keyword too :)


  • glenna

    Thanks, Holger. Great analysis.
  • PiGuy

    I get compiler C2299 behavior change: an explicit specialization cannot be a copy constructor or copy assignment operator.

    I am curious to know whether explicit specializations are not allowed only in this case or in general when it comes to operators.

    Anyhow writing a non-template copy assignment operator rectified the issue.

    Thanks to all for helping me. And to anyone who would bother to answer my implied question :)





  • MarcNelson

    I'm not exactly certain what you're trying to achieve, but obviously operator- will return a rvalue and a non-const reference can't bind to a temporary. I don't think there's anything special in this case that would require selection of the copy-assignment operator. Either way, it would be ill-formed at best.

    So you might want to define a copy-assignment operator with a const argument.

    The second specialization looks good, but you're missing the const. You could also let the compiler deduce arguments which requires you to omit the angle brackets.

    -hg


  • Joe_Cool

    No problem! One moment...


  • Compiler selecting template function in error?