How to 'partially' specialize member methods? - c++

I have a class
template<class T, bool isOrdered>
class Vector
{
public:
int Find(const T& t); // Return its index if found.
// Many other methods.
};
There are two versions of Find depending on the true or false of isOrdered. There is no partial specialization for member methods (class T is not specialized). My question is to how to specialize them? Thanks.

Use overload on std::integral_constant:
template<class T, bool isOrdered>
struct Vector {
int find(const T& t) {
return find_impl(t, std::integral_constant<bool,isOrdered>());
}
int find_impl (const T& t, std::true_type) {return 1;}
int find_impl (const T& t, std::false_type) {return 2;}
};

Related

Implicit type conversion for operator==

I'd like to have a way to compare different data types that are internally represented by an array (e.g. a string and a vector of chars) using a common array reference type. Consider the following code:
template <typename T>
struct ArrayConstRef {
const T *data;
size_t length;
};
template <typename T>
bool operator==(ArrayConstRef<T> a, ArrayConstRef<T> b);
template <typename T>
class ContainerA {
public:
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
template <typename T>
class ContainerB {
public:
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
int main() {
if (ContainerA<int>() == ContainerB<int>()) // error - no matching operator==
printf("equals\n");
return 0;
}
The overloaded operator== isn't matched even though the implicit conversion is available. Interestingly, if I removed the explicit keywords, the compiler manages to convert both objects to pointers and do the comparison that way (which I don't want). Why does one implicit conversion work but not the other? Is there a way to make it work?
This can be solved using SFINAE and little changes in code of your classes.
#include <cstddef>
#include <cstdio>
#include <type_traits>
template <typename T>
struct ArrayConstRef {
const T *data;
size_t length;
};
// This is needed to override other template below
// using argument depended lookup
template <typename T>
bool operator==(ArrayConstRef<T> a, ArrayConstRef<T> b){
/* Provide your implementation */
return true;
}
template <
typename Left,
typename Right,
// Sfinae trick :^)
typename = std::enable_if_t<
std::is_constructible_v<ArrayConstRef<typename Left::ItemType>, const Left&>
&& std::is_constructible_v<ArrayConstRef<typename Right::ItemType>, const Right&>
&& std::is_same_v<typename Left::ItemType, typename Right::ItemType>
>
>
inline bool operator==(const Left& a, const Right& b){
using T = typename Left::ItemType;
return ArrayConstRef<T>(a) == ArrayConstRef<T>(b);
}
template <typename T>
class ContainerA {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
template <typename T>
class ContainerB {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
int main() {
if (ContainerA<int>() == ContainerB<int>()) // no error :)
printf("equals\n");
return 0;
}
Compiles well with GCC 11.2 -std=c++17.
If you can use C++20, it is better to use concepts for this.
Code below compiles with GCC 11.2 -std=c++20.
#include <cstddef>
#include <cstdio>
#include <type_traits>
template <typename T>
struct ArrayConstRef {
const T *data;
size_t length;
};
// This is needed to override other template below
// using argument depended lookup
template <typename T>
bool operator==(ArrayConstRef<T> a, ArrayConstRef<T> b){
/* Provide your implementation */
return true;
}
template <typename Container>
concept ConvertibleToArrayConstRef =
requires (const Container& a) {
ArrayConstRef<typename Container::ItemType>(a);
};
template <
ConvertibleToArrayConstRef Left,
ConvertibleToArrayConstRef Right
>
requires (std::is_same_v<
typename Left::ItemType,
typename Right::ItemType>
)
inline bool operator==(const Left& a, const Right& b){
using T = typename Left::ItemType;
return ArrayConstRef<T>(a) == ArrayConstRef<T>(b);
}
template <typename T>
class ContainerA {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
template <typename T>
class ContainerB {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
int main() {
if (ContainerA<int>() == ContainerB<int>()) // no error :)
printf("equals\n");
return 0;
}
The problem is that operator== is a template, for it to be called the template parameter T needs to be deduced. But implicit conversion (ContainerA<int> -> ArrayConstRef<int> and ContainerB<int> -> ArrayConstRef<int>) won't be considered in template argument deduction.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
On the other hand, if you specify the template argument to bypass the deduction (in a ugly style), the code works.
if (operator==<int>(ContainerA<int>(), ContainerB<int>()))
Another workaround, you might change the parameter type and add a helper like:
template <typename T>
bool equal_impl(ArrayConstRef<T> a, ArrayConstRef<T> b);
template <template <typename> class C1, template <typename> class C2, typename T>
std::enable_if_t<std::is_convertible_v<C1<T>, ArrayConstRef<T>>
&& std::is_convertible_v<C2<T>, ArrayConstRef<T>>,
bool>
operator==(C1<T> a, C2<T> b) {
return equal_impl<T>(a, b);
}
LIVE

Wrapping a type, but preserving its behavior regarding function overloads?

I have a mechanism that uses function overloads to decompose a type into primitives. Here is a sample of the mechanism:
template <typename M, typename T> void process(M&, T&);
template <typename M, typename T>
void process_impl(M& m, const T& t) {
m.write(t);
}
template <typename M, typename T>
void process_impl(M& m, const std::vector<T>& v) {
for (const auto& t: v) {
process(m, t);
}
}
template <typename M, typename T>
void process(M& m, const T& t) {
process_impl(m, t);
}
The use case is to feed a type to process(), which will decompose it into simple types using the overloads of process_impl(). In order to customize the behavior of process() for a specific type, simply overload process():
struct media {
template <typename T> void write(const T& t) { std::cout << t << std::endl; }
};
void process(media& m, const std::string& s) {
process(m, s.size());
}
This code:
media m;
process(m, std::vector<std::string>{"1", "22"});
Outputs:
1
2
Now, I want to wrap media in a class, such that I can further tweak the behavior of process() for my wrapper structure, but I want to preserve the initial behavior of process() regarding media:
template <typename M>
struct debug_media {
template <typename T> void write(const T& t) { m.write(t); }
M m;
};
template <typename M, typename T>
void process(debug_media<M>& d, const T& t) {
std::cout << "debug: ";
process(d.m, t);
}
Ideally, I want the output to be:
debug: 1
debug: 2
But that is not the case, since the overload for debug_media is used only once, for the first invocation; afterward, the overloads of process() that accept a media are used, not the one with debug_media<media>.
I cannot have inheritance between media and debug_media. Is it possible to preserve the behavior of both overloads?
I have put sample code on ideone.
You can add the extra overload:
template <typename M, typename T> void process(debug_media<M>& d, const std::vector<T>& v)
{
for(const auto& t : v) {
process(d, t);
}
}
Live example

implementing a generic binary function with a class and functor as template parameters

I am trying to wrap some templated functions into some binary functors like below. When I try to compile the code I have the error
error: no match for call to ‘(QtyAsc) (myobj&, myobj&)
I thought that being operator() in QtyAsc a function in a class, the template deduction mechanism would have worked but it seems that the compiler doesn't accept myobj classes as valid types for it.
Is it maybe because of the call to boost::bind? I was trying to provide a default implementation for the second templated argument (unfortunately I cannot use C++11 with default templated arguments).
class myobj {
public:
myobj(int val) : qty_(val) {}
int qty() { return qty_;}
private:
int qty_;
};
template<class T>
int get_quantity(const T& o) {
throw runtime_error("get_quantity<T> not implemented");
}
template<>
int get_quantity(const myobj& o) {
return o.qty();
}
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr = boost::bind(&get_quantity<T>,_1)) const {
if(extr(o1) < extr(o2))
return true;
return false;
}
};
int main() {
myobj t1(10),t2(20);
QtyAsc asc;
if(asc(t1,t2))
cout << "Yes" << endl;
}
If you can't use C++11, just provide an additional overload:
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr) const {
return extr(o1) < extr(o2);
}
template<class T>
bool operator()(const T& o1, const T& o2) const {
return operator()(o1, o2, &get_quantity<T>);
}
};
(I've omitted the unnecessary boost::bind.) Also, you will need to declare myobj::qty to be const:
int qty() const {
return qty_;
}
since you want to invoke it on const objects. (Live demo)

Use operator! to negate an overloaded boolean predicate

Context: C++03 only + the use of boost is authorized
I'd like to raise the same question as in
How to negate a predicate function using operator ! in C++?
... but with an overloaded boolean predicate, that is:
struct MyPredicate
{
bool operator()(T1) const;
bool operator()(T2) const;
};
Clearly, MyPredicate cannot be derived from std::unary_function as it is impossible to define a single argument_type.
The aim is to use MyPredicate as argument to range adaptors, with a readable syntax like this:
using boost::for_each;
using boost::adaptors::filtered;
list<T1> list1;
list<T2> list2;
for_each(list1 | filtered(!MyPredicate()), doThis);
for_each(list2 | filtered(!MyPredicate()), doThat);
Of course, any solution involving explicit disambiguation is of no interest here.
Thank you in advance.
[ACCEPTED SOLUTION]
I'm using a slightly modified version of Angew's solution:
template <class Predicate>
struct Not
{
Predicate pred;
Not(Predicate pred) : pred(pred) {}
template <class tArg>
bool operator() (const tArg &arg) const
{ return !pred(arg); }
};
template <class Pred>
inline Not<Pred> operator! (const Pred &pred)
{
return Not<Pred>(pred);
}
template <class Pred>
Pred operator! (const Not<Pred> &pred)
{
return pred.pred;
}
Note that operators && and || can benefit from this trick likewise.
You can do this:
struct MyPredicate
{
bool positive;
MyPredicate() : positive(true) {}
bool operator() (T1) const {
return original_return_value == positive;
}
bool operator() (T2) const {
return original_return_value == positive;
}
};
inline MyPredicate operator! (MyPredicate p) {
p.positive = !p.positive;
return p;
}
To address your concern of forgetting to use positive, you could try an alternative approach with a wrapper class.
template <class Predicate>
struct NegatablePredicate
{
Predicate pred;
bool positive;
NegatablePredicate(Predicate pred, bool positive) : pred(pred), positive(positive) {}
template <class tArg>
bool operator() (const tArg &arg) const
{ return pred(arg) == positive; }
};
template <class Pred>
inline NegatablePredicate<Pred> operator! (const Pred &pred)
{
return NegatablePredicate<Pred>(pred, false);
}
You can also add an overload for optimisation purposes:
template <class Pred>
inline NegatablePredicate<Pred> operator! (const NegatablePredicate<Pred> &pred)
{
return NegatablePredicate<Pred>(pred.pred, !pred.positive);
}
To address possible concern with the wide scope of the template operator!, you can employ boost::enable_if magic.
You actually can derive from std::unary_function:
template<typename T>
struct MyPredicate : std::unary_function<T, bool>
{
bool operator()(T) const;
};

Using STL algorithms (specifically std::sort) from within a templated class

I've declared a template class MyContainer as bellow, then created an instance of it of type DataType1. The DataType1 class provides a friend function "DataSpecificComparison" which is used by std::sort to compare DataType1 objects. The program compiled and sorted correctly.
I then defined a class called DataType2, gave it a friend implementation of "DataSpecificComparison" and used it to create another instance of MyContainer.
I am now unable to compile the program as a "C2914: 'std::sort' : cannot deduce template argument as function argument is ambiguous" compile time error is reported.
How can a developer specify that the DataSpecificComparison binary predicate is to take arguments of template type T*? Or is there another way around this issue?
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
....
public:
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison)
}
}
class DataType1
{
....
friend bool DataSpecificComparison(const DataType1 * lhs, const DataType1 * rhs)
}
class DataType2
{
....
friend bool DataSpecificComparison(const DataType2* lhs, const DataType2* rhs)
}
You can use a temporary local function pointer variable of the required type to select the correct overload of DataSpecificComparison:
void SortMyContainerObjects()
{
typedef bool (*comparer_t)(const T*, const T*);
comparer_t cmp = &DataSpecificComparison;
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), cmp);
}
Here the compiler can deduce that you want to use the DataSpecificComparison overload that matches the comparer_t type, which resolves the ambiguity.
sth already gave a correct answer, but there's also a direct alternative based on the same principle:
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(),
static_cast<bool (*comparer_t)(const T*, const T*)>(&DataSpecificComparison));
}
This uses essentially the same mechanism. The cast forces overload resolution to happen before the Template Argument Deduction for std::sort.
template<typename T>
struct DataSpecificComp : public binary_function<T, T, bool>
{
public:
bool operator()(const T* lhs, const T* rhs)
{
return *lhs < *rhs;
}
};
call the sort function as shown below:
sort(vi.begin(), vi.end(), DataSpecificComp<int>());
I'd prefer something along the following lines: by default it compares objects with less_than (so you wouldn't have to remember to provide a function with a funny name), and there's an overload that allows giving your own comparison functor (again, value-based):
#include <vector>
#include <algorithm>
#include <functional>
template <class T, class Func>
struct indirect_binary_call_type: public std::binary_function<const T*, const T*, bool>
{
Func f;
indirect_binary_call_type(Func f): f(f) {}
bool operator()(const T* a, const T* b) const
{
return f(*a, *b);
}
};
template <class T, class Func>
indirect_binary_call_type<T, Func> indirect_binary_call(Func f)
{
return indirect_binary_call_type<T, Func>(f);
}
template <class T>
class MyContainer
{
private:
std::vector<T*> m_vMyContainerObjects;
public:
void Sort()
{
Sort(std::less<T>());
}
template <class Func>
void Sort(Func f )
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), indirect_binary_call<T>(f));
}
};
int main()
{
MyContainer<int> m;
m.Sort();
m.Sort(std::greater<int>());
}
Did you try defining DataSpecificComparison as template with bunch of specializations and giving it the type?
template<T>
bool DataSpecificComparison(const T* t1, const T* t2)
{
// something non compilable here
}
template<> bool DataSpecificComparison<Data1>(const Data1* t1, const Data1* t2)
{
// return *t1 < *t2;
}
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison<T>)
}
....
Templating DataSpecificComparison should work. You can also specifically call the proper std::sort template, but it's a bit cumbersome:
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
typedef bool (*compsT)(T, T);
public:
....
void SortMyContainerObjects()
{
std::sort<std::vector<T*>::iterator, compsT>(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison);
}
}