Andrea993
Andrea993

Reputation: 693

Why argument conversion is not considered when calling a templated function?

I have a template class and a friend operator* function

StereoSmp<TE> operator* (const StereoSmp<TE>& a, TE b)

I use it with TE=float but I need to multiply a StereoSmp<float> * double I think that should be possibile because it should converts double to float automatically and works but I get the error:

no match for ‘operator*’ (operand types are ‘StereoSmp<float>’ and
    ‘__gnu_cxx::__alloc_traits<std::allocator<double> >::value_type {aka double}’) 
deduced conflicting types for parameter ‘TE’ (‘float’ and ‘double’)

Why it doesn't convert double to float automatically? And what can I do to allow the automatic conversion between types?

Upvotes: 2

Views: 105

Answers (4)

Ben Voigt
Ben Voigt

Reputation: 283793

Although Yakk's answer is probably the best in this particular scenario, I want to point out that you can prevent this deduction conflict and get your expected result (pass StereoSmp<float>, deduce TE as float) by making the other argument ineligible for use in deduction:

StereoSmp<TE> operator* (const StereoSmp<TE>& a, remove_reference<TE>::type b)

Related reading: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3766.html

Upvotes: 2

davmac
davmac

Reputation: 20651

Why it doesn't convert double to float automatically? And what can I do to allow the automatic conversion between types?

This isn't a conversion problem, it's a template parameter inference issue. Since the declaration is of the form:

StereoSmp<TE> operator* (const StereoSmp<TE>& a, TE b)

... and the operands are of type StereoSmp<float> and double, the C++ inference rules do not work, because they do not take into account that a double is convertible to a float. These rules are specified by the language specification; the reason why they don't take potential conversions into account is probably because "it would make deduction very complicated, otherwise". The rules are already complex enough!

You can of course cast your double parameter to a float and it will work fine. Also, you could make the operator* a member function of StereoSmp, or you could independently parameterise the types of the type parameters:

template <class TE, class U> StereoSmp<TE> operator* (const StereoSmp<TE>& a, U b);

Upvotes: 0

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275800

Don't make your friend a template.

template<class TE>
struct StereoSmp {
  friend StereoSmp operator* (const StereoSmp& a, TE b) {
    return multiply( a, b ); // implement here
  }
};

this is a non-template friend to each type instance of the template StereoSmp. It will consider conversions.

Template functions don't consider conversions, they simply do exact pattern matching. Overload resolution is already insane enough in C++.

Upvotes: 5

YSC
YSC

Reputation: 40140

Keep it simple, silly:

template<class U>
StereoSmp<TE> operator* (const StereoSmp<TE>& a, U b);

or if it applies:

StereoSmp<TE> a {/* ... */};
double b = /* ... */;
auto c = a * static_cast<float>(b);

Why it doesn't convert double to float automatically?

Because template deduction happens before possible conversions are taken into consideration. If you call a*b with a a StereoSmp<float> and b a double, template substitution will fail before a float to double conversion can be considered, and name lookup will continue, until failing short of candidates.

This process is called Template argument deduction.

Upvotes: 3

Related Questions