problems with c++ set container - c++

When I try to compile the following code:
#include <iostream>
#include <set>
#include <vector>
using namespace std;
template <class T, class S>
class Property
{
public:
pair<T,S> p;
Property(T t, S s) { p = make_pair(t,s);}
};
int main()
{
set< Property<string, string> > properties;
Property<string, string> name("name", "Andy");
properties.insert(name);
}
I get the compilation error.
However, when I replace set by vector and hence use the the push_back function instead of insert function everything works fine. Could anyone explain me what am I doing wrong?
Thanks in advice.

std::set stores its values in a sorted binary tree, so it needs to know how to compare the values it holds. By default it uses std::less as a comparison function, which for un-specialized user defined types tries to call operator<. So, the easiest way to tell the set how to compare your objects is to define an operator< for your class:
template <class T, class S>
class Property
{
public:
pair<T,S> p;
Property(T t, S s) { p = make_pair(t,s);}
bool operator<(const Property<T,S>& rhs) const
{
return p < rhs.p;
}
};
However, there are also other ways of telling std::set how to compare your type. One is to specialize the std::less template for your class:
namespace std {
template<typename T,typename S>
struct less<Property<T, S> >
{
bool operator()(const Property<T, S>& lhs, const Property<T,S>& rhs) const
{
return lhs.p < rhs.p;
}
};
}
Another is to replace the default comparison type with a function with the correct signature, or a class that has an operator() defined with the correct signature. This is where things start to get ugly.
// Comparison function
template<typename T, typename S>
bool property_less_function(const Property<T,S>& lhs, const Property<T,S>& rhs)
{
return lhs.p < rhs.p;
}
// Comparison functor
template<typename T, typename S>
struct PropertyLess
{
bool operator()(const Property<T,S>& lhs, const Property<T,S>& rhs) const
{
return lhs.p < rhs.p;
}
};
int main()
{
// Set using comparison function.
// Have to pass the function pointer in the constructor so it knows
// which function to call. The syntax could be cleaned up with some
// typedefs.
std::set<Property<std::string, std::string>,
bool(*)(const Property<std::string, std::string>&,
const Property<std::string, std::string>&)>
set1(&property_less_function<std::string, std::string>);
// Set using comparison functor. Don't have to pass a value for the functor
// because it will be default constructed.
std::set<Property<std::string, std::string>, PropertyLess<std::string, std::string> > set2;
}
Keep in mind that whatever less-than function you use, that function must define a strict weak ordering for your type.

In order to insert something into std::set, you need to have operator< defined.
For example this compiles fine on GCC 4.7.2:
#include <iostream>
#include <set>
#include <vector>
using namespace std;
template <class T, class S>
class Property
{
public:
pair<T,S> p;
Property(T t, S s) {
p = make_pair(t,s);
}
bool operator<(const Property& p2) const {
//Something naive..
return p < p2.p;
}
};
int main()
{
set< Property<string, string> > properties;
Property<string, string> name("name", "Andy");
properties.insert(name);
}
An alternative would be to use std::unordered_set though that would require you to provide a hash for the key and defining operator==.

Related

std::set of MyElement with MyElement::SomeMethod as custom comparator

I have a simple MyElement class, and I would like to use a bool MyElement::SomeMethod(...) {...} as the custom comparator for a std::set of MyElement items.
I have made my research, and I am already aware of some alternative solutions, which I list below.
I also know how to change, for example, the comparator with std::greater instead of the default std::less, with code like this:
std::set<MyElement, std::greater<MyElement> > s;
My exact problem is that I want to use bool MyElement::SomeMethod(...) {...} as custom comparator.
The only solution I come up with is analogous to the last one in the list below, namely the solution for boolean function:
using Cmp = std::integral_constant<decltype(&MyElement::SomeMethod),
&MyElement::SomeMethod>;
std::set<MyElement, Cmp> my_set;
This solution only works for a static MyElement::SomeMethod, though.
I am wondering if there is an analgous, or more concise, way for a non static method.
List of alternative solutions:
method for C++20
auto cmp = [](const MyElement& lhs, const MyElement& rhs) { return ... };
std::set<MyElement, decltype(cmp)> s;
method for C++11
auto cmp = [](const MyElement& lhs, const MyElement& rhs) { return ... };
std::set<MyElement, decltype(cmp)> s(cmp);
function instead of a lambda
bool cmp(const MyElement& lhs, const MyElement& rhs) { return ...; }
and then
std::set<MyElement, decltype(cmp)*> s(cmp);
or
std::set<int, decltype(&cmp)> s(&cmp);
struct and operator()
struct cmp {
bool operator() (const MyElement& lhs, const MyElement& rhs) const {
return ...
}
};
and then
std::set<MyElement, cmp> s;
boolean function
bool cmp(const MyElement& lhs, const MyElement& rhs) {
return ...;
}
and then
#include <type_traits>
using Cmp = std::integral_constant<decltype(&cmp), &cmp>;
std::set<MyElement, Cmp> s;
This is a bit subjective, but to me the cleanest option is struct + operator() to match the definition of std::less, the default comparator for std::set. There's nothing wrong with the other options but a comparison functor is a common pattern and easy to recognize.
You could also define MyElement::operator<, and then you wouldn't need to pass in a comparator separately.
You can use std::mem_fn to bind a member function.
#include <functional>
#include <iostream>
#include <set>
#include <utility>
struct S {
int i;
bool cmp(const S& other) const { return i < other.i; }
};
// Define make function to avoid having to write out template types.
template <typename T, typename Cmp>
std::set<T, Cmp> make_set(Cmp&& cmp) {
return std::set<T, Cmp>{std::forward<Cmp>(cmp)};
}
int main(int argc, char* argv[]) {
auto s = make_set<S>(std::mem_fn(&S::cmp));
s.emplace(S{0});
std::cout << s.begin()->i << std::endl;
return 0;
}

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

Catch nondeterminism by error'ing on iterating pointer-keyed maps

It's been a few times we've found nondeterministic issues in the codebase I'm working on, and so far it's almost been root caused to the use of std::[unordered_]map/set<T*,U>, where the key is a pointer, combined with iteration on the map, usually in the form of a range-based for loop (since pointer values may change between executions, iteration order is nondeterministic).
I was wondering if there was some black template magic one could use to inject a static_assert when begin() is called on such a container. I think begin() is the best place to do this, or maybe iterator::operator++, since constructing iterators otherwise, such as a result of find(), is okay.
I thought I could overload std::begin, but the rules for range-based for loops state that .begin() is used if it exists. So, I'm out of ideas. Is there a clever trick to do this?
Further clarification: No custom comparator is involved, the direct value of the pointer (aka the address of the target object) is the key. This is fine for insertion and lookup, and only becomes a problem when iterating over the container since the order is based on unpredictable pointer values. I'm trying to find existing cases like this in a large existing codebase.
You can almost achieve the desired behavior with partial specializations:
20.5.4.2.1 The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.
Therefore, a simple specialization for std::map can be used to detect attempts to instantiate the template with a pointer key type:
#include <map>
namespace internal
{
// User-defined type trait
template<class Key, class T>
class DefaultAllocator
{
public:
using type = std::allocator<std::pair<const Key, T>>;
};
// Effectively the same as std::allocator, but a different type
template<class T>
class Allocator2 : public std::allocator<T> {};
}
namespace std
{
// Specialization for std::map with a pointer key type and the default allocator.
// The class inherits most of the implementation from
// std::map<Key*, T, Compare, ::internal::Allocator2<std::pair<Key*, T>>>
// to mimic the standard implementation.
template<class Key, class T, class Compare>
class map<Key*, T, Compare, typename ::internal::DefaultAllocator<Key*, T>::type> :
public map<Key*, T, Compare, ::internal::Allocator2<std::pair<Key*, T>>>
{
using base = map<Key*, T, Compare, ::internal::Allocator2<std::pair<Key*, T>>>;
using base::iterator;
using base::const_iterator;
public:
// Overload begin() and cbegin()
iterator begin() noexcept
{
static_assert(false, "OH NOES, A POINTER");
}
const_iterator begin() const noexcept
{
static_assert(false, "OH NOES, A POINTER");
}
const_iterator cbegin() const noexcept
{
static_assert(false, "OH NOES, A POINTER");
}
};
}
int main()
{
std::map<int, int> m1;
std::map<int*, int> m2;
// OK, not a specialization
m1[0] = 42;
for (auto& keyval : m1)
{
(void)keyval;
}
m2[nullptr] = 42; // Insertion is OK
for (auto& keyval : m2) // static_assert failure
{
(void)keyval;
}
}
However,
I haven't figured out a way to extend this for custom allocators: the declaration of the specialization has to depend on some user-defined type.
This is a terrible kludge, so I would only use it to find existing cases (rather than keeping as a static checker).
One approach to achieve a compile time failure for designated pointer types is to delete std::less, std::greater, std::hash, etc specializations for the specific pointer types that are susceptible to non-deterministic behavior (i.e. returned by interfaces). There are many options to provide "safe" functionality for pointer collections.
The following is a comprehensive example:
#include <cassert>
#include <memory>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#define DISABLE_NUMERIC_POINTER_SPECIALIZATIONS(T) \
namespace std { \
template <> struct hash<const T*> { std::size_t operator()(const T* obj) const = delete; }; \
template <> struct hash<T*> { std::size_t operator()(T* obj) const = delete; }; \
template <> struct less<const T*> { bool operator()(const T* lhs, const T* rhs) const = delete; }; \
template <> struct less<T*> { bool operator()(T* lhs, T* rhs) const = delete; }; \
template <> struct greater<const T*> { bool operator()(const T* lhs, const T* rhs) const = delete; }; \
template <> struct greater<T*> { bool operator()(T* lhs, T* rhs) const = delete; }; \
template <> struct less_equal<const T*> { bool operator()(const T* lhs, const T* rhs) const = delete; }; \
template <> struct less_equal<T*> { bool operator()(T* lhs, T* rhs) const = delete; }; \
template <> struct greater_equal<const T*> { bool operator()(const T* lhs, const T* rhs) const = delete; }; \
template <> struct greater_equal<T*> { bool operator()(T* lhs, T* rhs) const = delete; }; \
}
namespace NS {
class C {
public:
explicit C(int id) : m_id{id} {}
int id() const { return m_id; }
private:
int m_id;
};
inline bool operator ==(const C& lhs, const C& rhs) { return lhs.id() == rhs.id(); }
inline bool operator <(const C& lhs, const C& rhs) { return lhs.id() < rhs.id(); }
} // namespace NS
namespace std {
template <> struct hash<NS::C> { std::size_t operator()(const NS::C& obj) const { return obj.id(); } };
}
DISABLE_NUMERIC_POINTER_SPECIALIZATIONS(NS::C)
struct IndirectEqual {
template <typename T>
bool operator()(const T* lhs, const T* rhs) const {
return (lhs && rhs) ? *lhs == *rhs : lhs == rhs;
}
};
struct IndirectLess {
template <typename T>
bool operator()(const T* lhs, const T* rhs) const {
return (lhs && rhs) ? *lhs < *rhs : lhs < rhs;
}
};
struct IndirectGreater {
template <typename T>
bool operator()(const T* lhs, const T* rhs) const {
return (lhs && rhs) ? *lhs > *rhs : lhs > rhs;
}
};
struct IndirectHash {
template <typename T>
std::size_t operator()(const T* ptr) const {
return ptr ? std::hash<T>{}(*ptr) : std::numeric_limits<std::size_t>::max();
}
};
struct BuiltinLess {
template <typename T>
bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; }
};
struct SPLess {
template <typename T>
bool operator()(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) const { return lhs.get() < rhs.get(); }
};
struct BuiltinGreater {
template <typename T>
bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; };
};
struct PtrHash {
template <typename T>
std::size_t operator()(const T* ptr) const { return static_cast<std::size_t>(ptr); };
};
template <typename T>
class BasicSet : private std::set<T, BuiltinLess> {
public:
using std::set<T, BuiltinLess>::set;
using std::set<T, BuiltinLess>::find;
using std::set<T, BuiltinLess>::insert;
using std::set<T, BuiltinLess>::emplace;
using std::set<T, BuiltinLess>::end;
};
template <typename T>
class BasicSet<std::shared_ptr<T>> : private std::set<std::shared_ptr<T>, SPLess> {
public:
using std::set<std::shared_ptr<T>, SPLess>::set;
using std::set<std::shared_ptr<T>, SPLess>::find;
using std::set<std::shared_ptr<T>, SPLess>::insert;
using std::set<std::shared_ptr<T>, SPLess>::emplace;
using std::set<std::shared_ptr<T>, SPLess>::end;
};
int main()
{
// All of these decls result in a compiler error
// std::set<NS::C*> unsafe_s{new NS::C{1}, new NS::C{2}};
// std::map<NS::C*, int> unsafe_m{ {new NS::C{1}, 100} };
// std::unordered_set<NS::C*> unsafe_us{new NS::C{1}, new NS::C{2}};
// std::unordered_map<NS::C*, int> unsafe_um{ {new NS::C{1}, 123} };
std::set<NS::C*, IndirectLess> s{ new NS::C{1} };
std::unordered_set<NS::C*, IndirectHash> us1{ new NS::C{1} };
std::unordered_set<NS::C*, IndirectHash, IndirectEqual> us2{ new NS::C{1} };
auto c = new NS::C{1};
assert (s.find(c) != s.end());
assert (us1.find(c) == us1.end()); // pointers aren't equal
assert (us2.find(c) != us2.end()); // objects are equal
BasicSet<NS::C*> bs{ new NS::C{1} };
assert (bs.find(c) == bs.end()); // pointers aren't equal
auto sp1 = std::make_shared<NS::C>(10);
auto sp2 = std::make_shared<NS::C>(20);
BasicSet<std::shared_ptr<NS::C>> spset{sp1, sp2};
assert(spset.find(sp1) != spset.end());
return 0;
}
Note: This isn't perfect. E.G., one would need to disable 'volatile T*' and 'const volatile T*' variations. I'm sure there are other issues.

Overload comparison operators for a templated class

I'm having troubles in overloading comparison operators in order to compare two pair struct in such way:
typedef pair<string, unsigned int> INDEX;
bool operator>(INDEX &v1, INDEX &v2)
{
if(v1.second == v2.second) //if integer parts are equal
{
//string that comes earlier in the dictionary should be larger
return v1.first < v2.first;
}
return v1.second > v2.second;
}
The actual comparison takes place at this->element(hole/2) < this->element(hole) inside fixUp(CBTNODE hole), a member function of BinaryHeap class, which is a derived class of CompleteBinaryTree. The T will be instantiated as type INDEX, which is typedefed as pair<string, unsigned int>.
In other words, the comparison between two pairs: ("a.txt", 42) > ("b.txt", 42) should return true.
I tried to overload operator> outside the class declaration in two different ways but neither of them worked:
bool operator>(INDEX &v1, INDEX &v2);
bool operator>(BinaryHeap<T> &v1, BinaryHeap<T> &v2);
Any help will be much appreciated!
Z.Zen
Here is the declarations:
typedef int CBTNODE;
template <typename T>
class CompleteBinaryTree {
public:
//Initializes an empty binary tree
CompleteBinaryTree(int initialSize = 10);
//Destructor
~CompleteBinaryTree();
//Returns the element of the CBT pointed to by node. Behavior is undefined
//if node does not exist.
T element(CBTNODE node);
protected:
T *data;
int numElts, maxElts;
};
typedef pair<string, unsigned int> INDEX;
template <typename T>
class BinaryHeap : public CompleteBinaryTree<T>
{
public:
//Maintain heap property with bottom up heapify method.
void fixUp(CBTNODE hole);
};
bool operator>(INDEX &v1, INDEX &v2);
Implementation:
template <typename T>
T CompleteBinaryTree<T>::element(CBTNODE node) {
assert(node >= 0);
assert(node < numElts);
return data[node];
}
template <typename T>
void BinaryHeap<T>::fixUp(CBTNODE hole)
{
T tmp = this->element(hole);
while( hole > 0 && this->element(hole/2) < tmp )
{
//do stuff
}
}
bool operator>(INDEX &v1, INDEX &v2)
{
if(v1.second == v2.second) //if two have same relevance
{
return v1.first < v2.first;
}
return v1.second > v2.second;
}
A temporary, such as the result of element func, cannot be bound to a reference to non-const, such as the formal arguments of your operator>.
Declare it thusly:
bool operator>( INDEX const& v1, INDEX const& v2 )
However, the implementation that you present doesn't seem to be correct for operator>.
And while I'm at it, what you want is really operator< instead, because that's the one required by standard algorithms. Perhaps combined with an operator== (because it's inefficient to synthesize it from operator<). With those two any relationship can be checked for relatively efficiently.
Btw., if you stop using ALL UPPERCASE names for anything else then macros (see the FAQ), then you can avoid inadvertent name collision with macros.
Cheers & hth.,
Don't typedef INDEX, be explicit:
template<class F, class S>
struct Index {
std::pair<F, S> Value;
Index(const std::pair<F, S>& pValue)
: Value(pValue) {}
};
template<class F, class S>
bool operator<(const Index<F, S>& pLeft, const Index<F, S>& pRight) {
// your implementation...
}

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);
}
}