简体   繁体   中英

could not deduce template argument?

I have the following class

template<typename hi_t, typename lo_t>
struct int_t
{
hi_t hi;
lo_t lo;

int_t() : lo(0), hi(0) {}
int_t(int value) : lo(value), hi( value<0u? -1: 0 ) {}
int_t(unsigned value) : lo(value), hi( 0 ) {}

int_t& operator+=(const int_t& rhs)
{
    lo_t _lo = lo;

    lo += rhs.lo;
    hi += rhs.hi;
    hi += (int)(lo < _lo);

    return *this;
}

template<typename hi_t, typename lo_t>
inline friend int_t<hi_t, lo_t> operator+(const int_t<hi_t, lo_t>&, const int_t<hi_t, lo_t>&);
};

template<typename hi_t, typename lo_t>
int_t<hi_t, lo_t> operator+(const int_t<hi_t, lo_t>& lhs, const int_t<hi_t, lo_t>& rhs)
{ return int_t<hi_t, lo_t>(lhs) += rhs; }

when executing the following code

typedef int_t<long long, unsigned long long> int128;

int main()
{
    int128 i = 1024;
    i = i + 20;
}

the compiler produce the error:

'int_t<hi_t,lo_t> operator +(const int_t<hi_t,lo_t> &,const int_t<hi_t,lo_t> &)' : could not deduce template argument for 'const int_t<hi_t,lo_t> &' from 'int'

when i put the code of template operator inside the class body - with removing the template line from the friend operator - it works, but with friend operator outside the class it can't deduce the operator. i thought when compiler generates code for this template operator the input parameters and return value will be of type int128 so it should have no problem from casting from int to that type.

UPDATE

if we define the friend operator inside the class as following the previous example works

template<typename hi_t, typename lo_t>
struct int_t
{
hi_t hi;
lo_t lo;

int_t() : lo(0), hi(0) {}
int_t(int value) : lo(value), hi( value<0u? -1: 0 ) {}
int_t(unsigned value) : lo(value), hi( 0 ) {}

int_t& operator+=(const int_t& rhs)
{
    lo_t _lo = lo;

    lo += rhs.lo;
    hi += rhs.hi;
    hi += (int)(lo < _lo);

    return *this;
}

friend int_t operator+(const int_t& lhs, const int_t& rhs)
{ return int_t(lhs) += rhs; }

};

the problem happens when trying to define the template operator outside the class

The code is trickier than it seems at first look. The most tricky part is your declaration of a friend function. You should take a look at this answer regarding befriending a function from a template. The short recommendation is that you remove the templated operator+ and you implement it as a non-template friend function inside the class declaration:

template<typename hi_t, typename lo_t>
struct int_t
{
// ...
    friend int_t operator+(int_t lhs, const int_t& rhs ) {
        return lhs+=rhs;
    }
};

As of the particular error, it might not be that helpful, and it might even be confusing, but you can start by taking into account that a template will only be taken into account for operator overloading if after type deduction it is a perfect match (ie no conversion required). That means that int128_t + int will never be matched by a templated operator+ that has the same type for both left and right hand side, even if there is a conversion.

The proposed solution above declares (and defines) a non-template function. Because it is defined inside the class, it will only be considered by Argument Dependent Lookup, and will thus only apply when one of the operators is a int_t , if it is found by ADL, then it will be picked up for overload resolution with the usual non-template rules, which means that the compiler is able to use any possible conversions to both the lhs or rhs (one of them must be an int_t instantiation if it was found by ADL, but it will convert the other).

Turn on all your compiler warnings.

You are using the same template parameter names in your friend declarations as in the template class itself, which is not good; rename them. Here's one solution: Remove the out-of-line operator definition and make the inline definition this:

template<typename H, typename L>
inline friend int_t operator+(const int_t & lhs, const int_t<H, L> & rhs)
{
  return int_t(lhs) += rhs;
}

Now, since your RHS is an arbitrary type, you have to mention the type:

i = i + int128(20);

This is because there is no way to deduce parameters H,L from the integer 20 so that an appropriate conversion to int_t<H,L>(20) could be performed (see Nawaz's answer)!


To take advantage of your conversion constructor from int , you can only operate on the same type, not a templated other type. To this end, add a non-template operator:

int_t operator+(const int_t & rhs) const { return int_t(*this) += rhs; }

Now you can say i = i + 20; , using the int_t(int) constructor.

Update: As the OP suggests, to allow for symmetric invocation ( i = 50 + i; ), and in order to only allow operations within a fixed type as David suggests, we should remove both the unary operator and the templated friend binary operator and instead just have a non-templated binary friend:

friend int_t operator+(const int_t & lhs, const int_t & rhs) { return int_t(lhs) += rhs; }

That's a matter of design choice; I would personally favour the final version.

Are you certain that operator+= should be a member template? Usually, you just

inline friend int_t operator+(const int_t&, const int_t&) {...}

I'am not sure but maybe compiler needs template argument again

...
...
...

template<typename hi_t, typename lo_t>
int_t& operator+=(const int_t& rhs)
{
...
...
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM