A simple C++ OO question regrading templates and operator overloading: In the following class, I have overloaded the index operator twice:
template<class A, class B>
class test
{
A a1;
B a2;
public:
A& operator[](const B&);
B& operator[](const A&);
};
Now, if I instantiate an object of this template class with the same typenames:
test<int, int> obj;
calling the index operator will result in an error, because the two overloaded functions will have the same signatures.
Is there any way to resolve this issue?
Sorry, if this is a basic question. I am still learning!
You can add a partial specialization:
template<class A>
class test<A, A>
{
A a1, a2;
public:
A& operator[](const A&);
};
You can avoid this issue and make the code more robust and expressive by converting the index to some other type that clarifies what the user wants. Usage would be like this:
bidirectional_map<int, int> myTest;
int& valueFor1 = myTest[Key{1}];
int& key1 = myTest[Value{valueFor1}];
Implemented like this:
template<class TKey>
struct Key { const TKey& key; };
template<class TValue>
struct Value { const TValue& value; };
// Deduction guides (C++17), or use helper functions.
template<class TValue>
Value(const TValue&) -> Value<TValue>;
template<class TKey>
Key(const TKey&) -> Key<TKey>;
template<class TKey, class TValue>
class bidirectional_map
{
TKey a1; // Probably arrays
TValue a2; // or so?
public:
TValue & operator[](Key<TKey> keyTag) { const TKey & key = keyTag.key; /* ... */ }
TKey & operator[](Value<TValue> valueTag) { const TValue& value = valueTag.value; /* ... */ }
};
Now, Key and Value are popular names so having them "taken up" by these auxiliary functions is not the best. Also, this is all just a pretty theoretical exercise, because member functions are of course a much better fit for this task:
template<class TKey, class TValue>
class bidirectional_map
{
TKey a1; // Probably arrays
TValue a2; // or so?
public:
TValue& getValueForKey(const TKey& key) { /* ... */ }
TKey& getKeyForValue(const TValue& value) { /* ... */ }
};
In C++2a, you might use requires to "discard" the function in some case:
template<class A, class B>
class test
{
A a1;
B a2;
public:
A& operator[](const B&);
B& operator[](const A&) requires (!std::is_same<A, B>::value);
};
Demo
Here is an example solution using if constexpr that requires C++17:
#include <type_traits>
#include <cassert>
#include <string>
template <class A, class B>
class test
{
A a1_;
B b1_;
public:
template<typename T>
T& operator[](const T& t)
{
constexpr bool AequalsB = std::is_same<A,B>();
constexpr bool TequalsA = std::is_same<T,A>();
if constexpr (AequalsB)
{
if constexpr (TequalsA)
return a1_; // Can also be b1_, same types;
static_assert(TequalsA, "If A=B, then T=A=B, otherwise type T is not available.");
}
if constexpr (! AequalsB)
{
constexpr bool TequalsB = std::is_same<T,B>();
if constexpr (TequalsA)
return a1_;
if constexpr (TequalsB)
return b1_;
static_assert((TequalsA || TequalsB), "If A!=B, then T=A || T=B, otherwise type T is not available.");
}
}
};
using namespace std;
int main()
{
int x = 0;
double y = 3.14;
string s = "whatever";
test<int, int> o;
o[x];
//o[y]; // Fails, as expected.
//o[s]; // Fails, as expected
test<double, int> t;
t[x];
t[y];
//t[s]; // Fails, as expected.
return 0;
};
Related
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 wrote a class and I wanted to implement an iterator for it ( as shown in the following code ). I needed to overload a variety of operators and I faced the error mentioned below:
class BaseClass
{
virtual ~BaseClass() {}
};
template<class T>
class AbstractBaseOrgan: public BaseClass
{
public:
typedef T value;
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
protected:
value te;
};
template<typename Iter>
inline bool
operator<(const typename AbstractBaseOrgan<typename
std::iterator_traits<Iter>::value_type>::template
AbstractBaseIterator<Iter>& lhs,
const typename AbstractBaseOrgan<typename
std::iterator_traits<Iter>::value_type>::template
AbstractBaseIterator<Iter>& rhs)
{ return lhs.base() < rhs.base(); }
int main()
{
AbstractBaseOrgan<int>::AbstractBaseIterator<int*> temp;
AbstractBaseOrgan<int>::AbstractBaseIterator<int*> temp2;
int ttemp;
if(operator< (temp,temp2))
ttemp = 0;
return 0;
}
Compiling it gives me the following error:
error: no matching function for call to ‘operator<(AbstractBaseOrgan<int>::AbstractBaseIterator<int*>&, AbstractBaseOrgan<int>::AbstractBaseIterator<int*>&)’
Any idea what might cause this?
4 In most cases, the types, templates, and non-type values that are
used to compose P participate in template argument deduction. That is,
they may be used to determine the value of a template argument, and
the value so determined must be consistent with the values determined
elsewhere. In certain contexts, however, the value does not
participate in type deduction, but instead uses the values of template
arguments that were either deduced elsewhere or explicitly specified.
If a template parameter is used only in non-deduced contexts and is
not explicitly specified, template argument deduction fails.
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id.
You can avoid this by few ways. First way - make operator < friend for class AbstractIteratorBase, or its member.
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
public:
template<typename Iter>
friend bool operator < (const AbstractBaseIterator<Iter>& lhs, const AbstractBaseIterator<Iter>& rhs)
{
return lhs.base() < rhs.base();
}
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
Second variant is define AbstractBaseIterator class not in template class. And then typedef AbstractBaseIterator<T> iterator; in AbstractBaseOrgan. If you can use C++11 you can use something like this.
class BaseClass
{
virtual ~BaseClass() {}
};
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
template<typename Iter>
bool operator < (const AbstractBaseIterator<Iter>& lhs, const AbstractBaseIterator<Iter>& rhs)
{
return lhs.base() < rhs.base();
}
template<class T>
class AbstractBaseOrgan: public BaseClass
{
public:
typedef T value;
template<typename TT>
using iterator = AbstractBaseIterator<TT>;
protected:
value te;
};
int main()
{
AbstractBaseOrgan<int>::iterator<int*> temp;
AbstractBaseOrgan<int>::iterator<int*> temp2;
int ttemp;
if(operator< (temp,temp2))
ttemp = 0;
return 0;
}
I would like to overload the [] operator for a template class in respect with the template parameters. Like so:
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const Key1<T>& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const Key2<T>& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
I would use this class like so:
int main()
{
a_map<float, prop, key_a, key_b> pm;
}
Basically I want to be able to access the elements inside the _values vector without having to worry to much about the Key types. All that matters is that they have an index() member.
However I get the following error
error C2535: 'const Property &a_map::operator
[](const Key1 &) const' : member function already defined or
declared
even though key_a and key_b are two totaly different types class templates.
Am I missing something? Is the compiler afraid that in certain situation Key1<T> and Key2<T> might actually be the same type?
Edit
These are the class templates used in main
template<typename T>
struct prop
{
T weight;
T height;
};
template<typename T>
class key_a
{
public:
int index() { return _i; }
private:
int _i;
};
template<typename T>
class key_b
{
public:
int index() { return 3; } // Always return 3
Edit
I'm using MVC++ 2008 compiler.
Since both of your operator[] are the same except for the argument type, why not template them?
template <typename TT>
const Property<T>& operator[](const TT& k) const
{
return _values[k.index()];
}
You need to declare your index() functions as const because inside the a_map template you are invoking them through const objects
template<typename T> class key_a {
public:
int index() const // <- `const` is necessary
{ return _i; }
private:
int _i;
};
template<typename T> class key_b {
public:
int index() const // <- `const` is necessary
{ return 3; }
};
But otherwise, everything compiles and work fine for me.
Tried it in VS2010 compiler and get the same error as yours. This is obviously a compiler bug in MSVC++ compilers. Handling of template-template arguments is implemented incorrectly.
To work around the problem I was able to use this technique
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const typename Key1<T>::self& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const typename Key2<T>::self& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
and define the key templates as
template<typename T> class key_a {
public:
typedef key_a self;
int index() const { return _i; }
private:
int _i;
};
template<typename T> class key_b {
public:
typedef key_b self;
int index() const { return 3; }
};
This is inelegant, but it makes it work correctly with MSVC++ compilers.
Compiles fine like this... note how Prop/K1/K2 should be defined.
#include <vector>
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const Key1<T>& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const Key2<T>& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
template <typename T> struct K1 { int index() const { return 0; } };
template <typename T> struct K2 { int index() const { return 0; } };
template <typename T> struct Prop { };
int main()
{
a_map<float, Prop, K1, K2> m;
}
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);
}
}
If I want to create a function template, where the template parameter isn't used in the argument list, I can do it thusly:
template<T>
T myFunction()
{
//return some T
}
But the invocation must specify the 'T' to use, as the compiler doesn't know how to work it out.
myFunction<int>();
But, suppose I wanted to do something similar, but for the '[]' operator.
template
T SomeObject::operator [ unsigned int ]
{
//Return some T
}
Is there any way to invoke this operator?
This doesn't appear valid:
SomeObject a;
a<int>[3];
This should work:
class C
{
public:
template <class T>
T operator[](int n)
{
return T();
}
};
void foo()
{
C c;
int x = c.operator[]<int>(0);
}
But it's of no real value because you'd always have to specify the type, and so it looks like a very ugly function call - the point of an operator overload is to look like an operator invocation.
Boost.Program_options uses this neat syntax:
int& i = a["option"].as<int>();
Which is achieved with something like this:
class variable_value
{
public:
variable_value(const boost::any& value) : m_value(value) {}
template<class T>
const T& as() const {
return boost::any_cast<const T&>(m_value);
}
template<class T>
T& as() {
return boost::any_cast<T&>(m_value);
}
private:
boost::any m_value;
};
class variables_map
{
public:
const variable_value& operator[](const std::string& name) const
{
return m_variables[name];
}
variable_value& operator[](const std::string& name)
{
return m_variables[name];
}
private:
std::map<std::string, variable_value> m_variables;
};
You could adapt this idea to suit your own needs.
Like with any operator, the function name is operator#, so:
a.operator[]<int>(3);
You can use a.operator[]<int>(1);
But why do you want this?
This may not be an optimal solution, but you could directly call the operator as such:
a.operator[](3);
I tried this in g++ with the following test:
class MyClass {
public:
template<class T>
T operator[](unsigned int) {
// do something
return T();
}
};
int main(int argc, char* argv[]) {
MyClass test;
test.operator[]<int>(0);
//test<int>[0]; // doesn't compile, as you mentioned
return 0;
}
If you need to define operator[] then probably define the template at the class level. Something like this:
template<class T>
class C
{
public:
T operator[](int n)
{
return T();
}
};
int main()
{
C<int> c;
int x = c[0];
return 0;
}
I have a hard time coming up with an example where this would be needed (couldn't you just overload the operator instead?), but here's my thoughts anyway:
Since you cannot use the infix operator syntax with templatized operators, you might want to do the template instantiation before you call the operator. A proxy might be a way to do this.
class some_class {
private:
template<class T> class proxy {
some_class* that_;
public:
proxy(some_class* that) : that_(that) {}
T& operator[](std::size_type idx) {return that->get<T>(idx);}
};
template<class T> class const_proxy {
some_class* that_;
public:
proxy(const some_class* that) : that_(that) {}
const T& operator[](std::size_type idx) const {return that->get<T>(idx);}
};
template< typename T > proxy<T> get_array() {return proxy<T>(this);}
template< typename T > const_proxy<T> get_array() const {return proxy<T>(this);}
template< typename T > T& get(std::size_t idx) {/* whatever */}
template< typename T > const T& get(std::size_t idx) const {/* whatever */}
};
// This is a lousy use case.
// Did I already say I have a hard time imagining how to use this?
template< typename T >
void f(some_class& some_object, sid::size_t idx)
{
T& = some_object.get_array<T>()[idx];
}