What are the requirements to ensure that type equivalence can be used in the find function of a sorted container? Specifically how does mixed type comparison work.
Setup
Say I've got a struct, comparator, and set
struct Wrapper{int val;};
template<class T> struct compare {...};
template<class T> using wset=set<T,compare<T>>;
and a main function
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1}); //direct insertion
s.insert(Wrapper{10});
s.insert(11); //insertion by implicit construction
s.insert(100);
for (int x : s)
cout << x << ' ';
auto it= s.find(10);//find by equivalence
cout<<*it;
return 0;
}
I can get the code to run by adding constructors and conversion overloads to the wrapper struct, and also adding parameterized binary predicates to the compare struct. But I don't understand how its actually working.
Equivalence Implementation
From my understanding find(a) will be implemented by searching the set for an object b that satisfies !(a<b)&&!(b<a). therefore multi type equivalence should only require defining Ta<Tb and Tb<Ta ie
bool operator()(Ta a, Tb b){...}
bool operator()(Tb b, Ta a){...}
but in practice the compiler fails to make that comparison if that's all it has to go on.
Possible Options
Its seems like my only options are to
let the wrapper object be constructed by the incoming type
struct Wrapper{
Wrapper(int val):val{val}{};
int val;
};
template<class T>
struct compare {
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
};
which seems like it has an unnecessary object being constructed
to add cast operators to wrapper and the operators to compare mixed types
struct Wrapper
{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (T const & a, int const & b) const {return a.val < b;}
bool operator() (int const & a, T const & b) const {return a < b.val;}
};
which may allow unintentional implicit conversion in other places.
to add cast operators to wrapper, and an operator to compare one type at time.
struct Wrapper{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (int const & a, int const & b) const {return a < b;}
};
which like 2 may allow unintentional implicit conversion in other places.
Working examples
using a constructor, implicit conversion, and a less than operator to handle multiple types on insert and in find
#include <iostream>
#include <set>
using namespace std;
struct Wrapper{
Wrapper(int val):val{val}{};
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
};
template<class T>
using wset=set<T,compare<T>>;
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1});
s.insert(Wrapper{10});
s.insert(11);
s.insert(100);
for (int x : s)
cout << x << ' ';
auto it= s.find(10);
cout<<*it;
return 0;
}
this works but it feels like its probably constructing a wrapper in order to do the find
using implicit conversion, parameterizing the comparator operators, and making the comparator transparent prevents unintentional construction
#include <iostream>
#include <set>
using namespace std;
struct Wrapper{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (T const & a, int const & b) const {return a.val < b;}
bool operator() (int const & a, T const & b) const {return a < b.val;}
};
template<class T>
using wset=set<T,compare<T>>;
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1});
s.insert(Wrapper{10});
s.insert(Wrapper{11});
s.insert(Wrapper{100});
for (int x : s)
cout << x << ' ';
auto it= s.find(10);
cout<<*it;
return 0;
}
but requires that for every type combination there is a converter from WT to T and a operator for both (WT,T) and (T,WT). The converter also can't be made explicit so it exposes potential accidental conversion
defining conversion between types and handle only one type per operator
#include <iostream>
#include <set>
using namespace std;
struct Wrapper{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (int const & a, int const & b) const {return a < b;}
};
template<class T>
using wset=set<T,compare<T>>;
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1});
s.insert(Wrapper{10});
s.insert(Wrapper{11});
s.insert(Wrapper{100});
for (int x : s)
cout << x << ' ';
auto it= s.find(10);
cout<<*it;
return 0;
}
which is simpler than option 2 but still relies on implicit conversion.
Related
I am trying to create a vector of maps. Each map has a different comparator.
Here is what I have tried:-
#include<iostream>
#include<map>
#include<vector>
template <class T>
struct head {
virtual bool operator() (const T& x, const T& y) const = 0;
};
template <class T>
struct greater: head<T> {
bool operator() (const T& x, const T& y) const {return x>y;}
};
template <class T>
struct less: head<T> {
bool operator() (const T& x, const T& y) const {return x<y;}
};
int main()
{
std::vector<std::map<int, int, head<int>>> mp;
return 0;
}
But I am getting an error that my operator() is pure virtual in head.
Please tell me what is the correct way to achieve this?
You need a single comparator type that works for all your vectors. Like this:
template<typename T>
struct comp {
comp(bool gt) : do_greater(gt) {}
bool operator() (const T& x, const T& y) const
{
return do_greater ? x > y : x < y;
}
bool do_greater;
};
int main()
{
std::vector<std::map<int, int, comp<int>>> mp;
mp.emplace_back(false); // first map uses less-than
mp.emplace_back(true); // first map uses greater-than
}
That is, the choice of comparison function needs to be driven by the state initialized in each std::map constructor. That single ?: branch is probably better for performance than calling a virtual method every time, too.
Consider the following set of classes and the relationship of their operators: We can implement them in two distinct ways. The first where the operators are defined within the class, and the latter where they are defined outside of the class...
template<typename T>
struct A {
T value;
T& operator+(const A<T>& other) { return value + other.value; }
// other operators
};
temlate<typename T>
struct B {
T value;
T& operator+(const B<T>& other) { return value + other.value; }
};
// Or...
template<typename T>
struct A {
T value;
};
template<typename T>
T& operator+(const A<T>& lhs, const A<T>& rhs) { return lhs.value + rhs.value; }
// ... other operators
template<typename T>
struct B {
T value;
};
template<typename T>
T& operator+(const B<T>& lhs, const B<T>& rhs) { return lhs.value + rhs.value; }
// ... other operators
Is there any way in C++ where I would be able to make a single class or struct of operators to where I could simply be able to declare or define them within any arbitrary class C without having to write those same operators multiple times for each class? I'm assuming that the operators will have the same behavior and property for each distinct class that defines them considering that they will all follow the same pattern.
For example:
template<typename T, class Obj>
struct my_operators {
// define them here
};
// Then
template<typename T>
struct A {
T value;
my_operators ops;
};
template<typename T>
struct B {
T value;
my_operators ops;
};
Remember I'm restricting this to C++17 as I'm not able to use any C++20 features such as Concepts... If this is possible, what kind of method or construct would I be able to use, what would its structure and proper syntax look like? If this is possible then I'd be able to write the operators once and just reuse them as long as the pattern of the using classes matches without having to write those operators for each and every individual class...
What about using CRTP inheritance?
#include <iostream>
template <typename T>
struct base_op
{
auto operator+ (T const & o) const
{ return static_cast<T&>(*this).value + o.value; }
};
template<typename T>
struct A : public base_op<A<T>>
{ T value; };
template<typename T>
struct B : public base_op<B<T>>
{ T value; };
int main()
{
A<int> a1, a2;
B<long> b1, b2;
a1.value = 1;
a2.value = 2;
std::cout << a1+a2 << std::endl;
b1.value = 3l;
b2.value = 5l;
std::cout << b1+b2 << std::endl;
}
Obviously this works only for template classes with a value member.
For the "outside the class" version, base_op become
template <typename T>
struct base_op
{
friend auto operator+ (T const & t1, T const & t2)
{ return t1.value + t2.value; }
};
-- EDIT --
The OP asks
now I'm struggling to write their equivalent +=, -=, *=, /= operators within this same context... Any suggestions?
It's a little more complicated because they must return a reference to the derived object... I suppose that (for example) operator+=(), inside base_op, could be something as
T & operator+= (T const & o)
{
static_cast<T&>(*this).value += o.value;
return static_cast<T&>(*this);
}
Taking the answer provided by user max66 using CRTP and borrowing the concept of transparent comparators provided by the user SamVarshavchik within the comment section of my answer, I was able to adopt them and came up with this implementation design:
template<class T>
struct single_member_ops {
friend auto operator+(T const & lhs, T const & rhs)
{ return lhs.value + rhs.value; }
friend auto operator-(T const & lhs, T const & rhs)
{ return lhs.value - rhs.value; }
template<typename U>
friend auto operator+(T const& lhs, const U& rhs)
{ return lhs.value + rhs.value; }
template<typename U>
friend auto operator-(T const& lhs, const U& rhs )
{ return lhs.value - rhs.value;}
};
template<typename T>
struct A : public single_member_ops<A<T>>{
T value;
A() = default;
explicit A(T in) : value{in} {}
explicit A(A<T>& in) : value{in.value} {}
auto& operator=(const T& rhs) { return value = rhs; }
};
template<typename T>
struct B : public single_member_ops<B<T>> {
T value;
B() = default;
explicit B(T in) : value{in} {}
explicit B(B<T>& in) : value{in.value} {}
auto& operator=(const T& rhs) { return value = rhs; }
};
int main() {
A<int> a1(4);
A<int> a2;
A<int> a3{0};
a2 = 6;
a3 = a1 + a2;
B<double> b1(3.4);
B<double> b2(4.5);
auto x = a1 + b2;
auto y1 = a2 - b2;
auto y2 = b2 - a1;
return x;
}
You can see that this will compile found within this example on Compiler Explorer.
The additional templated operator allows for different types: A<T> and B<U> to use the operators even if T and U are different for both A and B provided there is a default conversion between T and U. However, the user will have to be aware of truncation, overflow & underflow, and narrowing conversions depending on their choice of T and U.
I've written a class Number that contains only one attribute: T value. I'm currently learning about templates, so T is the data type. What I want to achieve is doing the following sort of computation.
Number<int>(2) + Number<double>(1.2)
What I have so far can do a operation, but it fails when there are two different datatypes. So far I've written this:
//class template
template<class T>
class Number
{
public:
T value;
Number(T num1)
{
value = num1;
}
Number<T> operator + ( const Number<T> &other) const
{
return Number<decltype(value+other.value)> (value+other.value);
}
};
It only does the arithmic operation when the datatypes are the same:
Questions:
Why does the program only work with the same datatypes?
This can I answer for a part by myself. I use the line:
Number<T> operator + ( const Number<T> &other) const
So if the left handside is of type int. Every T becomes an int. I don't know how I need to change it without getting an error.
What do I need to fix in order to do computations with different
datatypes?
Edit:
A constraint is that the template may contain only one type argument
Besides declaring a friend operator+ with two template parameters as suggested, you can also place a secondary template for the member function operator+, which allows you to do casting plus.
template<typename T>
class Number
{
public:
T value;
Number(const T&num1)
{
value = num1;
}
template <typename X> auto operator + ( const Number<X> &other) const
{
auto c = this->value + other.value;
return Number<decltype(c)> ( c );
}
};
#include <iostream>
int main()
{
Number<int> n{2};
Number<double> a{3.4};
std::cout << (a+n).value << std::endl;
}
Or, you may use a friend function (I think that this is more symbolic consistent.)
template<typename T>
class Number
{
public:
T value;
Number(T num1)
{
value = num1;
}
Number& operator +=( const Number<T> &other)
{
this->value += other.value;
return *this;
}
};
template <typename T1,typename T2> auto operator+(const Number<T1>&a, const Number<T2>&b)
{
auto c = a.value + b.value;
return Number<decltype(c)>( c );
}
#include <iostream>
#include <type_traits>
template <typename T>
struct Number {
Number(T _value = T(0)) : value(_value) {}
template <typename S>
Number(const Number<S>& n) : value(n.value) {}
T value;
template <typename S>
friend Number<S> operator+(const Number<S>& a, const Number<S>& b);
};
template <typename S>
Number<S> operator+(const Number<S>& a, const Number<S>& b) {
return Number<S>{a.value + b.value};
}
int main() {
std::cout << operator+<typename std::common_type<int, double>::type>(Number<int>{1}, Number<double>{1.2}).value << std::endl;
return 0;
}
this is a c++11 implementation. operator+ contains exactly one argument. I hope this is what you want.
Problem Description and Question
I have a template class Class1. It contains in map in which I want to insert structures A or B.
The problem is that the structures A and B have different types of member variables. Structure A has an std::string member variable whereas structure B has an int member variable.
The comparator is based on structure A. So obviously when I want to insert a structure B it will not compile.
Class1<B,B> c2;
c2.AddElement({1},{1});
How can I fix that design Issue? For instance is it possible to keep Class1 as template class and do something to TestCompare?
I also have a constraint. I cannot modify the structures A and B. they are written in C code. I have no right to change them because they are external codes used by other users. I just simplified the code as much as possible.
Source Code
The code was compiled on cpp.sh
#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;
template<typename T1, typename T2> class Class1 {
public :
struct TestCompare {
bool operator()(const T1 & lhs, const T1 & rhs) const {
return lhs.a < rhs.a;
}
};
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 & value) {
m.emplace(key, value);
}
private :
std::map<T1,T2,TestCompare> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});
// Problem here. Obviously it will not compile because the Operator is using
// the member variable of struct A.
//Class1<B,B> c2;
//c2.AddElement({1},{1});
//return 0;
}
New Source code
// Example program
#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;
bool operator<(const A & lhs, const A & rhs) {
return lhs.a < rhs.a;
}
bool operator<(const B & lhs, const B & rhs) {
return lhs.b < rhs.b;
}
template<typename T1, typename T2> class Class1 {
public :
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 value) {
m.emplace(key, value);
}
std::map<T1,T2> getMap() {
return m;
}
private :
std::map<T1,T2> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});
// Problem here. Obviously it will not compile because the Operator is using
// the member variable of struct A.
Class1<B,B> c2;
c2.AddElement({1},{1});
c2.AddElement({2},{2});
for(const auto &e: c2.getMap()) {
std::cout << e.first.b << " " << e.first.b << std::endl;
}
return 0;
}
I guess you could remove TestCompare from Class1 and template that.
template<typename T> struct TestCompare {
bool operator()(const T & lhs, const T & rhs) const {
// default implementation
return lhs < rhs;
}
};
template<typename T1, typename T2> class Class1 {
...
private :
std::map<T1,T2,TestCompare<T1>> m;
}
You could then specialise TestCompare for A and B
template<> struct TestCompare<A> {
bool operator()(const A & lhs, const A & rhs) const {
return lhs.a < rhs.a;
}
};
template<> struct TestCompare<B> {
bool operator()(const B & lhs, const B & rhs) const {
return lhs.b < rhs.b;
}
};
EDIT:
Actually you could just use std::less instead of TestCompare. It amounts to pretty much the same thing, and std::map uses std::less by default.
TestCompare requires that every type you use must have a member a that can be compared using <. That's a lot of requirements, which implies a terrible design. Add a 3rd template parameter that will be used to pass a function or a functor that compares the objects
struct CompareA {
bool operator()(A const & lhs, A const & rhs) const {
return lhs.a < rhs.a;
}
};
struct CompareB {
bool operator()(B const& lhs, B const& rhs) const {
/*...*/
}
};
template<typename KeyT, typename ValueT, typename Compare> class Dict {
public :
Class1() {}
~Class1() {}
void AddElement(KeyT const & key, ValueT const & value) {
m.emplace(key, value);
}
private :
std::map<KeyT, ValueT, Compare> m;
};
Dict<A, B, CompareA> dictA;
Dict<B, B CompareB> dictB;
You could specialize the struct TestCompare, like john has suggested in his answer, and provide it as the default template argument
template<typename KeyT, typename ValueT, typename Compare = TestCompare<KeyT>> class Dict { /*...*/ };
Such solution will allow you to provide only 2 arguments, like so
Dict<B, B> dict;
while still maintaining the ability to provide another comparer if necessary.
I know that it's possible to define a hash function for a struct X by defining a separate hash function struct:
struct hash_X {
size_t operator()(const X &x) const {}
bool operator()(const X &a, const X &b) const {}
};
int main() {
unordered_set<X, hash_X, hash_X> s;
}
But I'm looking for something like operator<, which can be attached to struct X itself, e.g. with set:
struct X {
bool operator<(const X &other) const {}
};
int main() {
set<X> s;
}
The end goal is something like:
struct X {
size_t operator()(void) const {}
bool operator()(const X &other) const {}
};
int main() {
unordered_set<X> s;
}
Is this possible in C++?
std::unordered_set is defined within std namespace. And it uses std::hash structures to hash many different types. If you want to be able to use std::unordered_set<X> (without adding much info to the declaration), you must create another overload of the std::hash template, so as to make it hash your structure.
You should be able to get it working by doing the following:
# include <unordered_set>
struct X {
size_t operator()(void) const {}
bool operator()(const X &other) const {}
};
namespace std {
template<>
struct hash<X> {
inline size_t operator()(const X& x) const {
// size_t value = your hash computations over x
return value;
}
};
}
int main() {
std::unordered_set<X> s;
}
Andalso, you must provide either an overload to std::equal_to, or a comparison operator (operator==()) for your structure. You should add one of the following:
struct X {
...
inline bool operator==(const X& other) const {
// bool comparison = result of comparing 'this' to 'other'
return comparison;
}
};
Or:
template <>
struct equal_to<X> {
inline bool operator()(const X& a, const X& b) const {
// bool comparison = result of comparing 'a' to 'b'
return comparison;
}
};
There is no hash operator, but you could hide the hash struct inside of your X:
struct X
{
std::string name;
struct hash
{
auto operator()( const X& x ) const
{ return std::hash< std::string >()( x.name ); }
};
};
You could even make it a friend and make name private, etc.
Live example
namespace hashing {
template<class T>
std::size_t hash(T const&t)->
std::result_of_t<std::hash<T>(T const&)>
{
return std::hash<T>{}(t);
}
struch hasher {
template<class T>
std::size_t operator()(T const&t)const{
return hash(t);
}
};
}
the above is some boilerplate that sets up an adl-based hash system.
template<class T>
using un_set=std::unordered_set<T,hashing::hasher>;
template<class K, class V>
using un_map=std::unordered_map<K,V,hashing::hasher>;
now creates two container aliases where you do not have to specify the hasher.
To add a new hashable:
struct Foo {
std::string s;
friend size_t hash(Foo const&f){
return hashing::hasher{}(s);
}
};
and Foo works in un_set and un_map.
I would add support for std containers and tuples into hashing namespace (override the function hash for them), and magically those will work too.
I'd recommend to consider a more general hash class. You could define for this class all the common hash manipulation operations that you could need:
struct ghash {
// all the values and operations you need here
};
Then in any class where you want to compute a hash, you could define a conversion operator
struct X {
operator ghash () { // conversion operator
ghash rh;
// compute the hash
return rh;
}
};
You can then easily calculate the hash:
X x;
ghash hx = x; // convert x to a hash
hx = (ghash)x; // or if you prefer to make it visible
This will make it easier to extend the use of your hash structure without reinventing the common ground for any other struct X, Y,Z that may need a hash in the future.
Live demo here