How should one overload functions with const/non-const parameters? - c++

I have the following code:
// string specializations
void foo(const char *a, const char *b);
void foo(const char *a, const std::string &b);
void foo(const std::string &a, const char *b);
void foo(const std::string &a, const std::string &b);
// generic implementation
template<typename TA, typename TB>
void foo(TA a, TA b)
{...}
The problem is that this test case:
char test[] = "test";
foo("test", test);
ends up calling the templated version of foo. Obviously, I can just add a few more overloads with various mixes of non-const parameters, but I want to know: is there's a better way to overload foo such that it is specialized on all const and non-const pairings of strings? One that doesn't require me to hope I haven't missed some permutation of argument types?

Thanks to Mooing Duck's suggestion, this is my solution:
// string specialization
void foo(const std::string &a, const std::string &b);
template<typename TA, typename TB>
typename std::enable_if<
std::is_constructible<std::string, TA>::value &&
std::is_constructible<std::string, TB>::value
>::type foo(TA a, TB b)
{
foo(std::string(std::move(a)), std::string(std::move(b)));
}
// generic implementation
template<typename TA, typename TB>
typename std::enable_if<
!std::is_constructible<std::string, TA>::value ||
!std::is_constructible<std::string, TB>::value
>::type foo(TA a, TB b)
{...}

If I understand your goals properly you could do this
template<typename T1,typename T2 >
void foo(T1, T2)
{
static_assert(sizeof(T1) == 0,"Did not overload all foo's");
}
template<>
void foo<const char *a, const char *b>()
{
... Handle this case
}
... ect ect ect
This has the advantage of you handling every instance you want explicitly while generating a compilation error if you missed one.

Related

Comparator for sort, specialization

In a template function a std::vector shall get sorted. T can be a simple type or a std::pair e.g.,
std::vector<double> or
std::vector<std::pair<int,Something> >
When T is a pair then only the first element shall be compared. How can I implement the comparator for the two cases?
I have tried:
template<typename T>
inline bool smaller(const T& a,const T& b)
{
return a<b;
}
template<typename T,typename S>
inline bool smaller(
const std::pair<T,S>& a,
const std::pair<T,S>& b
)
{
return a.first<b.first;
}
template<typename T> inline void function(std::vector<T >& vVec)
{
...bla...
sort(vVec.begin(),vVec.end(),smaller<T>);
...bla...
}
but it does not work this way. I have also tried specialization but I do not find the right syntax to specialize the smaller() function.
You could just wrap it in a lambda:
std::sort(vVec.begin(),vVec.end(), [](const auto& a, const auto& b) { return smaller(a, b); });
One easy work around is to make both of your smaller functions opeator()'s of a smaller struct. Using
struct smaller
{
template<typename T>
bool operator()(const T& a,const T& b)
{
return a < b;
}
template<typename T, typename S>
bool operator() (const std::pair<T, S>& a, const std::pair<T, S>& b)
{
return a.first < b.first;
}
};
allows you to just pass a smaller to sort like
template<typename T> inline void function(std::vector<T >& vVec)
{
sort(vVec.begin(),vVec.end(),smaller{});
}
and in sort overload resolution will kick in on the two operator() smaller has and for any std::vector<std::pair>, the std::pair overload will be called.

How does function template deduce types with const qualifier?

I have the following code with outputs. I have two questions.
1) foo1 < double, char* >(a,b); is not able to compile by gcc 4.1.2.
It cannot instantiate it.
2) foo1(a,b); outputs AB, while foo(a,b) can output Ac. Is it because of the const qualifier?
Code:
#include <iostream>
template<typename A, typename B>
class C {
public:
void operator()(A a, B b) {
std::cout << "AB" << std::endl;
}
};
template<typename A>
class C<A, const char*> {
public:
void operator()(A a, const char* b);
};
template<typename A>
void C<A, const char*>::operator()(A a, const char* b) {
std::cout << "Ac" << std::endl;
}
template<typename A, typename B>
void foo(A a, B b) {
C<A,B>()(a,b);
}
template<typename A, typename B>
class C1 {
public:
void operator()(A a, const B b) {
std::cout << "AB" << std::endl;
}
};
template<typename A>
class C1<A, char*> {
public:
void operator()(A a, const char* b);
};
template<typename A>
void C1<A, char*>::operator()(A a, const char* b) {
std::cout << "Ac" << std::endl;
}
template<typename A, typename B>
void foo1(A a, B b) {
C1<A,B>()(a,b);
}
int main() {
double a = 0;
const char *b;
C<double, const char*>()(a,b);
foo<double, const char*>(a,b);
foo(a,b);
C1<double, char*>()(a,b);
//foo1<double, char*>(a,b);
foo1(a,b);
return 0;
}
Outputs:
Ac
Ac
Ac
Ac
AB
When you call foo1(a, b) with b being a const char*, the type is deduced as const char*. The const applies to the type being pointed at, not the pointer itself. It can't drop the const because it is a vital part of the type. The specialization of C1 is for char*. They don't match, and so the non-specialized version is used.
Also, in your specialized version of C1, your operator's signature isn't the same as it is in the non-specialized one.
//In C1<A, B>
void operator()(A a, const B b) //If B was char*, this could be char* const
//In C1<A, char*>
void operator()(A a, const char* b) //This is const char*

How to prevent implicit instantiation of a template function

I have a class with several strongly typed enums and I want them to be implicitly comparable to int. For that purpose I have written super-simple template function:
template<class T> bool operator==(const T &e, const int &i)
{
return (static_cast<int>(e) == i);
}
and the opposite one
template<class T> bool operator==(const int &i, const T &e)
{
return (e == i);
}
However this might be dangerous because it pretty much writes a blank check for any comparison between class T and an integer. I would therefore want only few explicitly specified instantiations such as
template bool operator==(const MyEnum &e, const int &i);
template bool operator==(const int &i, const MyEnum &e);
and no other. Is there a way to disable all implicit instantiations or achieve this? I can always write both operators for each enum separately of course but templates really seem like a better solution.
Use SFINAE to reject overloads which don't deduce a MyEnum:
template<class T, std::enable_if_t<std::is_same<T, MyEnum>::value>* = nullptr>
bool operator==(const T &e, const int &i);
template<class T, std::enable_if_t<std::is_same<T, MyEnum>::value>* = nullptr>
bool operator==(const int &i, const T &e);
Due to the comments I have updated my answer. If you want to only accept select few template types then I suggest using this bool_or trick:
#include <type_traits>
#include <utility>
template<bool... Bs>
using bool_sequence = std::integer_sequence<bool, Bs...>;
template<bool... Bs>
using bool_and = std::is_same<bool_sequence<Bs...>,
bool_sequence<(Bs || true)...>>;
template<bool... Bs>
using bool_or = std::integral_constant<bool, !bool_and<!Bs...>::value>;
template<class T, class... Ts>
using any_same = bool_or<std::is_same<T, Ts>::value...>;
template<class T, std::enable_if_t<any_same<T, MyEnum, X, Y, Z>::value>* = nullptr>
bool operator==(const T &e, const int &i);
template<class T, std::enable_if_t<any_same<T, MyEnum, X, Y, Z>::value>* = nullptr>
bool operator==(const int &i, const T &e);
You can add any number of types to compare with.

how to implement arithmetic operator for boost::variant so that it supports different numeric types

For example, if I have the following variant and a static_visitor:
typedef boost::variant<long, int, float, double> DataType;
And I'd like this to work:
DataType x(1.2);
DataType y(100);
std::cout<<boost::apply_visitor(Multiply(), x, y)<<std::endl; // should return 120
with a visitor like this:
struct Multiply : public boost::static_visitor<DataType>
{
template<typename T, typename U>
DataType operator()(const T& a, const U& b) const
{
// what to do here??
}
template<typename T>
DataType operator()(const T& a, const T& b) const
{
return a * b;
}
};
How I can do this without having to list all the possible combinations of types in the Multiply visitor?
My advice: Don't special-case on same type, but on multiplyability:
struct Multiply : public boost::static_visitor<DataType> {
template<typename T, typename U>
static auto operator()(const T& a, const U& b) const
-> decltype(DataType(a*b)){
return a*b;
}
template<typename... Ts>
static DataType operator()(const Ts... params) const {
throw std::invalid_argument("can't multiply");
}
};
Changes:
No special-casing of any combination which works.
Throwing an exception on any combination which does not.
Now static.
Uses expression-SFINAE to decide.
Still, it won't return 120 for your example, because binary floating-point cannot represent 1.2 exactly.

How to pass an operator as default functor argument?

For simplicity, let's say I want to do implement a function which takes two parameters and predicate which test for equality,
template<typename T, typename TCompare>
bool Eq(const T& a, const T& b, const TCompare& cmp) {
return cmp(a, b);
}
but I also want that operator== is assumed if predicate is not passed.
I tried several different approaches, but all resulted in either syntax error or "operator==" not defined. I even tried replacing the Eq function with a
Nevertheless, I suppose what I want is possible since the big part of STL supports not passing the optional TCompare parameter (std::sort, std::set ...).
UPDATE:
Thanks to levis501 suggestion, this is the best way I have found so far:
template<typename T, typename TCompare>
bool Eq(const T& a, const T& b, const TCompare& cmp) {
return cmp(a, b); // or some lengthy code
}
template<typename T>
bool Eq(const T& a, const T& b) {
static std::equal_to<T> EqCmp;
return Eq(a, b, EqCmp);
}
So, you basically need to create a wrapper which passes std::equal_to, or any other operator-equivalent functor. For some reason, having it as a default argument does not compile when you invoke the function without it.
How about std::equal_to ? http://www.cplusplus.com/reference/std/functional/equal_to/
You can overload functions, no?
// Use the user-supplied TCompare.
template<typename T, typename TCompare>
bool Eq(const T& a, const T& b, TCompare cmp) {
return cmp(a, b);
}
// Use op== for equality otherwise.
template<typename T>
bool Eq(const T& a, const T& b) {
return a == b;
}
If the code calls Eq() with three arguments, the compiler will resolve the call to the first overload. If the code calls Eq() with only two arguments, then the compiler will have to resolve to the second overload.
I don't suppose something like the following would work for you:
#include <functional>
template<typename T, typename TCompare = std::equal_to<T>>
class Math {
public:
static bool Eq(const T& a, const T& b) {
TCompare cmp;
return cmp(a, b);
}
};
int _tmain(int argc, _TCHAR* argv[]) {
Math<int>::Eq(1,1);
return 0;
}
Default template parameters are only allowed on class templates (or at least that's what MSVC 10 says)