Reputation: 261
the question posed in: Type condition in template
is very similar, yet the original question wasn't quite answered.
#include "stdafx.h"
#include <type_traits>
class AA {
public:
double a;
double Plus(AA &b) {
return a + b.a;
}
};
template<class T> double doit(T &t) {
if (std::is_same<T, AA>::value)
return t.Plus(t);
else
return t + t;
}
int _tmain(int argc, _TCHAR* argv[])
{
double a;
AA aa;
doit(a);
doit(aa);
return 0;
}
This doesn't compile, nor did I expect it to. Is something like this possible? Being, based on the template value, I want some code to be compiled, and others not. Here, 'double' doesn't have a method called "Plus" and class "AA" doesn't override the '+' operator. Operator overloading isn't always desirable when considering subtle semantics to the operations, so I'm looking for an alternative. I'd prefer to do #ifdef's (truly conditional compilation as posed in the ref'd question), but based on template values.
Upvotes: 11
Views: 10931
Reputation: 735
Since C++17 there is static if which is called if-constexpr. The following compiles fine since clang-3.9.1 and gcc-7.1.0, latest MSVC compiler 19.11.25506 handles well too with an option /std:c++17.
template<class T> double doit(T &t) {
if constexpr (std::is_same_v<T, AA>)
return t.Plus(t);
else
return t + t;
}
Upvotes: 15
Reputation: 261
This code compiles and runs:
#include <boost/type_traits/is_floating_point.hpp>
#include <boost/type_traits/is_class.hpp>
#include <boost/utility/enable_if.hpp>
using namespace boost;
class AA {
public:
double a;
AA Plus(AA &b) {
AA _a;
_a.a = a + b.a;
return _a;
}
};
template<class T>
typename disable_if<is_floating_point<T>, T >::type
doit(T &t) {
return t.Plus(t);
}
template<class T>
typename enable_if<is_floating_point<T>, T >::type
doit(T &t) {
return t+t;
}
int _tmain(int argc, _TCHAR* argv[])
{
double a;
AA aa;
doit(a);
doit(aa);
return 0;
}
Upvotes: 2
Reputation: 16737
What you want is a static if. C++ doesn't have it. There are many ways to work around it, none as good as native support. In addition to the methods specified in the other two answers, you could try tag dispatch.
template<class T> double doitImpl(T &t, std::true_type) {
return t.Plus(t);
}
template<class T> double doitImpl(T &t, std::false_type) {
return t+t;
}
template<class T> double doit(T &t) {
return doitImpl(t, std::is_same<T, AA>);
}
Upvotes: 5
Reputation: 75668
double doit(AA &t) {
return t.Plus(t);;
}
template<class T> double doit(T &t) {
return t + t;
}
Your code doesn't work because if the template is deduced as AA
then t + t
inside the body is ill-formed. On the other hand if T
is deduces as double
then t.Plus(t)
becomes ill-formed.
To better understand what is happening: A template is instantiated for each template type is called with.
doIt(a)
instantiates doIt
with T = double
:
double doIt<double>(double &t) {
if (false)
return t.Plus(t); // <-- syntax error
else
return t + t;
}
doIt(aa)
instantiates doIt
with T = AA
:
double doIt<AA>(AA &t) {
if (true)
return t.Plus(t);
else
return t + t; // <-- syntax error
}
You should avoid specializing function templates because functions overload. You can read this excellent Herb Sutter article: Why Not Specialize Function Templates?
Upvotes: 3
Reputation: 60969
Overloading?
template<class T>
double doit(T &t) {
return t + t;
}
double doit(AA &t) {
return t.Plus(t);
}
Explicit specialization is also possible, though superfluous:
template<class T>
double doit(T &t) {
return t + t;
}
template<>
double doit<>(AA &t) {
return t.Plus(t);
}
Upvotes: 3