Reputation: 765
I have a curious issue with template, I am trying to make a basic addition between a template class and "float/double/int" type. It is very basic but if I do:
template<class T>
class toto{
T a;
};
template<class T>
toto<T> operator+(toto<T> const&, T&){
std::cout << "hello " <<std::endl;
}
int main(){
toto<float> t;
toto<float> d = t +2.3;
}
It will not compile because 2.3 is considered like a double, it does not match the signature. I could use a second template parameter for my operator+ as
template<class T, class D>
toto<T> operator+(toto<T> const&, D&){
std::cout << "hello " <<std::endl;
}
It compiles, execute correctly but too dangerous D can be everything. An alternative is to create different signature with float, double or int (O_O). Boost::enable_if seems my solution but in the doc I read:
template <class T>
T foo(T t,typename enable_if<boost::is_arithmetic<T> >::type* dummy = 0);
Apply this method to the operator* doest not work, because the compiler complains default arguments are forbidden.
Any suggestions ?
Cheers,
++t
Upvotes: 4
Views: 150
Reputation: 60999
Use a non-deduced context for the second parameter. And a const
-reference as the parameter, to allow rvalues.
template <typename T> struct identity {using type = T;};
template <typename T>
using identity_t = typename identity<T>::type;
template<class T>
toto<T> operator+(toto<T> const&, identity_t<T> const&)
{
std::cout << "hello " <<std::endl;
}
A non-deduced context will cause deduction to ignore the call argument for a certain parameter as the invoked template parameters cannot be deduced. In some scenarios, as here, that is desired since inconsistent deductions are not possible anymore. In other words, the type of the second parameter fully depends on the first argument of the call, not the second (which may be implicitly converted).
toto<float> d = t + 2.3;
Should now compile, Demo.
Upvotes: 5
Reputation: 11014
The enable_if
s tend not be too beautiful to read but other means feel worse.
I typically use return value type for enabling a function because it is located most
up front:
#include<type_traits>
#include<iostream>
template<class T>
class toto
{
T a;
};
template<typename T,typename D>
typename std::enable_if< std::is_arithmetic<D>::value
// && some other constraint
// && etc.
, toto<T> >::type operator+(toto<T> const&, D const&)
{
std::cout << "hello " <<std::endl;
}
int main()
{
toto<float> t;
toto<float> d = t +2.3;
}
Upvotes: 1
Reputation: 41321
You can use enable_if
like this:
template<class T, class D, typename = typename std::enable_if<std::is_arithmetic<D>::value>::type>
toto<T> operator+(toto<T> const&, const D&){
std::cout << "hello " <<std::endl;
return toto<T>();
}
Upvotes: 1