I have the following two functions in C.
float floatAdd(float a, float b)
{
return a+b;
}
double doubleAdd(double a, double b)
{
return a+b;
}
Now, I would like to combine both functions and write a tempalte function in C++ as follows.
template<class T>
T add(T a, T b)
{
// if T=float, return floatAdd(a,b)
// if T=double, return doubleAdd(a,b)
}
Since the return types are different, I am unable to find a solution!
Please note that the above functions are very simple, and used as merely an example. My intention is provide a C++ template wrapper for some Legacy C functions the kind explained the example.
Your example doesn't really show the need to use the specific functions. But in a more general case, you can provide a specific implementation for a template function or template class for a given set of template arguments. The feature is known as Template specialization and is specifically designed to address this problem.
template<class T>
T add(T a, T b);
// Special implementation for floatAdd
template<>
float add<float>(float a, float b)
{
return floatAdd(a, b);
}
// Special implementation for floatDouble
template<>
double add<double>(double a, double b)
{
return doubleAdd(a, b);
}
For such a simple case, you can simply use a regular template :
template<class T>
T add(T a, T b)
{
return a + b;
}
If you don't care about supporting a general case, you can just use function overloading and forget about templates :
float add(float a, float b)
{
return floatAdd(a, b);
}
double add(double a, double b)
{
return doubleAdd(a, b);
}
Related
In a project I'm working on I have a templated function similar to this where all of the arguments should be of type T
#include <iostream>
template<typename T> bool aWithinBOfC(T a, T b, T c)
{
return std::abs(a - c) < b;
}
the issue I'm having is it won't compile if all of the arguments are not of the same type but it seems reasonable that it should implicitly cast similar types to the one with the highest resolution before evaluation. Is there any way to get a call like this to be valid?
int main()
{
double a{1.2};
double b{1.4};
float c{0.1f};
std::cout << aWithinBOfC(a, b, c) << std::endl;
}
Something along these lines, perhaps:
template<typename T>
bool aWithinBOfCImpl(T a, T b, T c) {
/* actual implemenattion */
}
template <typename ... Args>
auto aWithinBOfC(Args... args) {
return aWithinBOfCImpl<std::common_type_t<Args...>>(args...);
}
Demo
You don’t need implicit conversions at the call site. The compiler will implicitly convert the types to the largest one in the expression in the return statement.
template <class T, class U, class V>
bool aWithinBOfC(T a, U b, V c) {
return std::abs(a - c) < b;
}
What is the correct way to perform == and != operators in template classes?
Assume this code:
template<typename T>
class C {
T x, y;
public:
C(T a, T b) : x(a), y(b) {}
bool cmp() {
return x == y;
}
};
int main()
{
// OK
C<int> i(1,2);
i.cmp();
// not OK
C<double> d(1.0, 2.0);
d.cmp();
return 0;
}
If you build it with g++ -Wfloat-equal, you'll get
warning: comparing floating point with == or != is unsafe
[-Wfloat-equal]
because you can't simply compare float variables.
Update
I've solved the problem using type_traits and enable_if like this (thanks #Andrew and #OMGtechy):
#include <type_traits>
#include <limits>
#include <cmath>
#include <iostream>
using namespace std;
template <typename IntegralType>
typename std::enable_if<std::is_integral<IntegralType>::value, bool>::type
equal(const IntegralType& a, const IntegralType& b) {
return a == b;
}
template <typename FloatingType>
typename std::enable_if<std::is_floating_point<FloatingType>::value, bool>::type
equal(const FloatingType& a, const FloatingType& b) {
return std::fabs(a-b) < std::numeric_limits<FloatingType>::epsilon();
}
template<typename T>
class C {
T x, y;
public:
C(T a, T b) : x(a), y(b) {}
bool cmp() {
return equal(x, y);
}
};
int main()
{
// OK
C<int> i(1,2);
cout << i.cmp() << endl;
// not OK
C<double> d(1.0, 1.0);
cout << d.cmp() << endl;
return 0;
}
This question seems to be asking two things:
How can I do floating point comparisons without using operator==, and
how can I modify the behaviour of a template depending on the type passed to it.
One answer to the second question is to use type traits. The code below demonstrates this for your situation, providing a comparison_traits for general types (using ==) and a specialisation for doubles, using a tolerance (which answers the first question, too).
#include <cmath>
template <typename T> struct comparison_traits {
bool equal(const T& a, const T& b) {
return a == b;
}
// etc.
};
template<> struct comparison_traits<double> {
bool equal(const double& a, const double& b) {
return fabs(a - b) < 1e-15; // or whatever...
}
};
template <typename T>
class C {
T x, y;
public:
C(const T& a, const T& b) : x(a), y(b) {}
bool cmp() {
return comparison_traits<T>::equal(x, y);
}
};
int main() {
// OK
C<int> i(1, 2);
i.cmp();
// Now OK too...
C<double> d(1.0, 2.0);
d.cmp();
return 0;
}
Other options include:
Providing a template parameter that allows you to specify a comparison function, defaulting to std::equal_to
Specialising your template for double, so that you can write a different implementation of cmp()
It depends how it will be used. Comparing floats properly depends on the context.
I would recommend what #Niall says: add a comparator template parameter, defaulting to std::equal_to. This will allow callers to control how values are compared. See for example the docs on std::sort to see how a comparator parameter is used. The downside to this is that it's the caller's responsibility to account for comparing floats. If they forget, then they'll get the compiler warning you see.
Another option is template class specialization. Make a specialization for your class to deal specifically with float or double types, and compare them differently using whatever logic you prefer. Probably not the best solution though. The benefit to this is that callers no longer need to remember to specify a comparator.
If you ask why you get this warning:
here some example:
double a,b;
a=10.0/13.0;
b = a/3;
b*=3;
std::cout<<"a="<<a<<std::endl;
std::cout<<"b="<<b<<std::endl;
if(a!=b){
std::cout<<"NOT equal!!"<<std::endl;
std::cout<<"a-b="<<a-b<<std::endl;
}
else
std::cout<<"equal"<<std::endl;
if you'll do the math a and b are clearly equal.
but this is the output I've got:
a=0.769231
b=0.769231
NOT equal!!
a-b=-1.11022e-016
because it is not so accurate, a proper comparison for double should define some tolerancy:
for example(tolerancy may change according needs):
int compare(double a, double b)
{
double tolerancy = 0.000001;
if (abs(a-b) < tolerancy) return 0;
else if (a>b) return 1;
else /*if (a<b)*/ return -1;
}
and if I use this compare i get:
a=0.769231
b=0.769231
equal
struct F
{
int operator()(int a, int b) { return a - b; }
bool operator()(long a, long b) { return a == b; }
};
F f;
int x = 104;
bind<int>(f, _1, _1)(x); // f(x, x), i.e. zero
Some compilers have trouble with the bind(f, ...) syntax. For portability reasons, an alternative way to express the above is supported:
boost::bind(boost::type<int>(), f, _1, _1)(x);
like above, the code use a boost::type for function object type,
I to know where contain the boost::type implementation?
I looked around and found the definition below.
// type.hpp
namespace boost {
// Just a simple "type envelope". Useful in various contexts, mostly to work
// around some MSVC deficiencies.
template <class T>
struct type {};
}
Recently I was working a friend who wanted to make C++ more Haskell-y, and we wanted a function that's basically like this:
auto sum(auto a, auto b) {
return a + b;
}
Apparently I can't use auto as a parameter type, so I changed it to this:
template<class A, class B>
auto sum(A a, B b) {
return a + b;
}
But that doesn't work either. What we eventually realized we need this:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b) {
return a + b;
}
So my question is, what's the point? Isn't decltype just repeating information, since the compiler can just look at the return statement?
I considered that maybe it's needed so we can just include a header file:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
... but we can't use templates like that anyway.
The other thing I considered was that it might be easier for the compiler, but it seems like it would actually be harder.
Case 1: With decltype
Figure out the type of the decltype statement
Figure out the types of any return values
See if they match
Case 2: Without decltype
Figure out the types of any return values
See if they match
So with those things in mind, what's the point of the trailing return type with decltype?
Well - time passed since the original question was asked and the answer now is that you can!
Yes, it is true that the question is tagged C++11 - with which you still cannot do what the OP is asking for. But it's worthwhile to show what is doable with C++14 and later.
Since C++14 this is valid:
template<class A, class B>
auto sum(A a, B b) {
return a + b;
}
And since C++20 this is also valid:
auto sum(auto a, auto b) {
return a + b;
}
The following is the C++11 answer, kept here for historical reasons, with some comments from the future (C++14 and later):
What if we have the following:
template<class A, class B, class C>
auto sum(A a, B b, C c) {
if (rand () == 0) return a + b;
// do something else...
return a + c;
}
.. where a + b and a + c expressions yield different type of results.
What should compiler decide to put as a return type for that function and why?
This case is already covered by C++11 lambdas which allow to omit the return type as long as return statements can be deduced to the same type (NB standard quote needed, some sources claim only one return expression is allowed and that this is a gcc glitch).
A note from the future (C++14 and on): the example above is still not valid, you may only have a single possible return type. However if there are different return types but the actual return type can be deduced at compile type, then we have two different functions, which is valid. The following for example is valid since C++17:
template<class A, class B, class C>
auto sum(A a, B b, C c) {
if constexpr(std::is_same_v<A, B>) return a + b;
else return a + c;
}
int main() {
auto a1 = sum(1, 2l, 3.5); // 4.5
auto a2 = sum(1, 2, 3.5); // 3
}
Back to the original C++11 answer, explaining why the requested syntax is not supported:
A technical reason is that C++ allows the definition and declaration to be separate.
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
The definition of the template could be in the header. Or it could be in another file, so that you don't have to wade through pages and pages of function definitions when looking through an interface.
C++ has to account for all possibilities. Restricting trailing return types to just function definitions means that you can't do something as simple as this:
template<class A, class B>
class Foo
{
auto sum(A a, B b) -> decltype(a + b);
}
template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
A note from the future (C++14 and on): you still cannot have a declaration with auto return type, if the definition is not available when the compiler sees the call.
but we can't use templates like that anyway.
First, trailing return types aren't purely a template thing. They work for all functions. Secondly, says who? This is perfectly legal code:
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b);
template<class A, class B>
auto sum(A a, B b) -> decltype(a + b)
{
}
The definition of the template could be in the header. Or it could be in another file, so that you don't have to wade through pages and pages of function definitions when looking through an interface.
C++ has to account for all possibilities. Restricting trailing return types to just function definitions means that you can't do something as simple as this:
template<class A, class B>
class Foo
{
auto sum(A a, B b) -> decltype(a + b);
}
template<class A, class B>
auto Foo<A, B>::sum(A a, B b) -> decltype(a + b)
{
}
And this is fairly common for many programmers. There's nothing wrong with wanting to code this way.
The only reason lambdas get away without the return type is because they have to have a function body defined with the definition. If you restricted trailing return types to only those functions where the definition was available, you wouldn't be able to use either of the above cases.
There is no technical reason why it is not possible. The main reason they haven't is because the C++ language moves very slowly and it takes a very long time for features to be added.
You can nearly get the nice syntax you want with lambdas (but you can't have templacised arguments in lambdas, again for no good reason).
auto foo = [](int a, double b)
{
return a + b;
};
Yes, there are some cases where the return type could not be automatically deduced. Just like in a lambda, it could simply be a requirement to declare the return type yourself in those ambiguous cases.
At the moment, the restrictions are just so arbitrary as to be quite frustrating. Also see the removal of concepts for added frustration.
In a blog post from Dave Abrahams, he discusses a proposal for having this function syntax:
[]min(x, y)
{ return x < y ? x : y }
This is based off of possible proposal for polymorphic lambdas. He has also started work here on updating clang to support this syntax.
Is it somehow possible to reflect the type of the argument of a function at compile time?
So that
int b = add(3, 6)
Would result in a template instantiation in the form of
int add(int a, int b) { return a + b }
out of some however declared function template
A add(A a, B b) { return a + b }
I don't know if that is possible with templates they do not really seem to be made for heavy meta-programming.
template<typename T>
T add(T a, T b) { return a + b; }
Then you can call this with any built-in type (int, short, double, float, etc), and it will instantiate the function add at compile time, according to the type you use.
Note that if you split this into header/source, you will have trouble:
add.h:
template<typename T>
T add(T a, T b);
add.cpp:
template<typename T>
T add(T a, T b) { return a + b; }
main.cpp:
#include "add.h"
int a = 3;
int b = 5;
int i = add(a, b);
When you try to compile this, it will fail at link time. Here's why.
Compiling add.obj does not instantiate the add template method -- because it's never called in add.cpp. Compiling main.obj instantiates the function declaration -- but not the function body. So at link time, it will fail to find the definition of the add method.
Simplest fix is to just put the entire template function in the header:
add.h:
template<typename T>
T add(T a, T b) { return a + b; }
and then you don't even need the add.cpp file at all.
Doesn't this do what you're asking?
template <typename A, typename B>
A add(A a, B b) { return a + b; }
This is hardly "heavy meta-programming".
This is exactly the sort of thing templates do.
template <typename T>
inline T add(T a, T b) { return a + b; }
Allowing two different types gets a little bit trickier but is also possible.
If you plan on using anything other than simple types you want (in a header file):
template <typename A>
inline A add(const A& a, const A& b) { return a + b; }
Note the 'inline'.
As noted by others, the issue with mixed types is how to determine the return type from the argument types. Suppose we stick to simple types and have:
template
inline A add(A a, B b) { return a + b; }
Then this fails (likely with only a warning):
double d = add(1, 1.5); // Sets d to 2.0
So you have to do some work. For example:
template<class A, class B>
struct Promote
{
};
template<class A>
struct Promote<A,A>
{
typedef A Type;
};
template<>
struct Promote<int, double>
{
typedef double Type;
};
template<>
struct Promote<double, int>
{
typedef double Type;
};
The add function becomes:
template<class A, class B>
inline typename Promote<A,B>::Type add(A a, B b)
{
return a + b;
}
What all this does for you is ensure that the return type is the one you specify for adding a given pair of types. This will work even for complex types.
Templates do all their magic at compile time, but that seems to be quite adequate for what you're asking:
template <class T>
T add(T a, T b) { return a + b; }
It does get a bit more complex when the two might not match, so (for example) you could add an int to a double and get a double result. The current standard doesn't really support that1; with C++0x you can look into auto and decltype for such tasks.
1Though Boost typeof will often do the job.