Inherit operators defined outside class - c++

In this minimal example I have a class A with an operator + defined outsise of it:
template<class T> class A {};
template<class T1, class T2> void operator+(A<T1> a, A<T2> b) {}
template<class T> class B : public A<T> {};
int main(int, char**) {
B<int> a, b;
a + b;
return 0;
}
I've tried to create an implicit conversion from B to A but it requires the operator+ to be a friend of A and be defined inside A which will cause problems when more than one instance of A<...> gets instantiated.
So, is there any other way to do this without having to define the operator+ again?
Thank you in advance for any help.

template<class T> class A {
template<class T2>
friend void operator+(A const& lhs, A<T2> const& rhs) {}
};
template<class T> class B : public A<T> {};
int main(int, char**) {
B<int> a, b;
a + b;
return 0;
}
this works. The assymetry in + (one template, one not) ensure that multiple As don't conflict with their +.
In some situations you really need lhs to be an instance of B:
template<class T> struct A {
template<class D, class T2, std::enable_if_t<std::is_base_of<A, D>{}, bool> =true >
friend void operator+(D const& lhs, A<T2> const& rhs) {
std::cout << D::name() << "\n";
}
static std::string name() { return "A"; }
};
template<class T> struct B : public A<T> {
static std::string name() { return "B"; }
};
int main(int, char**) {
B<int> a, b;
a + b;
return 0;
}
which uses A's operator+, but the LHS is of type B. Doing this for B<T2> on the right hand side isn't very viable, it gets ridiculous.

Related

Global scope friend operator declaration when class in namespace and using templated type as return type

I am struggling with friend statement for a templated operator and namespaces.
Sorry if I'm a bit long but I want to give a good description of my issue.
First, some context. Forget about the namespace at present.
I have a class A and a public operator that needs to access its private member:
template<typename U>
struct B { U valb; };
template<typename U>
struct C { U valc; };
template<typename U,typename V>
struct A
{
private:
U v1; V v2;
template<typename T1,typename T2>
friend A<T1,T2> operator * ( const B<T2>&, const C<T1>& );
};
template<typename T1,typename T2>
A<T1,T2>
operator * ( const B<T2>& b, const C<T1>& c )
{
A<T1,T2> a;
a.v1 = c.valc * b.valb; // dummy
return a;
}
int main()
{
B<float> b;
C<int> c;
auto a = b * c;
}
This builds fine.
Now for some reason I want to put class A in a namespace, (mostly to remove it from the public API, user code will use "sub" types, declared with a using declaration).
Now the trouble begins.
I am building on this answer, that covers that topic and works fine.
It explains that I need to forward declare the class, then the operator, and in the friend declaration, prefix the operator with ::.
The only difference between the situation described in that linked question and mine is the return type. In my case, it is a templated type. This seems to be the trouble (or is it?)
So I tried that (online here):
template<typename U>
struct B { U valb; };
template<typename U>
struct C { U valc; };
// forward declaration of class A
namespace ns {
template<typename U,typename V> struct A;
}
// forward declaration of operator
template<typename T1,typename T2>
ns::A<T1,T2>
operator * ( const B<T2>&, const C<T1>& );
namespace ns {
template<typename U,typename V>
struct A // class declaration
{
template<typename T1,typename T2>
friend A<T1,T2> ::operator * ( const B<T2>&, const C<T1>& );
private:
U v1; V v2;
};
} // namespace
// operator definition
template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>& b, const C<T1>& c )
{
ns::A<T1,T2> a;
a.v1 = c.valc * b.valb; // dummy
return a;
}
int main()
{
B<float> b;
C<int> c;
auto a = b * c;
}
This fails to build with:
error: ISO C++ forbids declaration of 'operator*' with no type [-fpermissive]
23 | friend A<T1,T2> ::operator * ( const B<T2>&, const C<T1>& );
And if I remove the ::, then the operator is not recognized as a friend.
What am I doing wrong? How can I manage that issue?
Unless the befriend function template is declared already, I don't think you define this function template outside the class definition: essentially, there is no way to actually name the operator. To make things a bit more interesting, the two parameter types operated on are actually in a different namespace, i.e., the operator*() really needs to be defined in a different namespace than ns. It seems this does the trick:
template<typename U> struct B { U valb; };
template<typename U> struct C { U valc; };
// declaration of ns::A to declare the operator*
namespace ns { template<typename U,typename V> struct A; }
template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>&, const C<T1>& );
namespace ns {
template<typename U,typename V>
struct A {
template<typename T1,typename T2>
friend auto ::operator * ( const B<T2>&, const C<T1>& ) -> A<T1, T2>;
private:
U v1; V v2;
};
}
template<typename T1,typename T2>
ns::A<T1,T2> operator * ( const B<T2>& b, const C<T1>& c ) {
ns::A<T1,T2> a;
a.v1 = c.valc * b.valb; // dummy
return a;
}
int main()
{
B<float> b;
C<int> c;
auto a = b * c;
}
For a live demo see this Compiler Explorer link.

C++ template class operator overloading with the same signatures

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

Design issue with template map with takes different structures

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.

Template class with operator overload

Here is my question about template class
aclass<int> A{1,2};
aclass<float> B{3.0,4.0};
aclass<int> C;
int main()
{
C=A+B; //How to overload this operator in a simple way?
B=A; //And also this?
return 0;
}
How can I overload operator to handle template class with different types?
(Sorry for my poor English)
You can have member function templates inside your class template:
template <typename T>
struct aclass
{
aclass(aclass const &) = default;
aclass & operator=(const aclass &) = default;
template <typename U>
aclass(aclass<U> const & rhs) : a_(rhs.a_), b_(rhs.b_) {}
template <typename U>
aclass & operator=(aclass<U> const & rhs)
{
a_ = rhs.a_;
b_ = rhs.b_;
return *this;
}
T a_, b_;
};

Template friend function and return type deduction

Note: This question is really close to Return type deduction for in-class friend functions, but I did not find the answer to my problem there.
Tested with clang 3.4 with std=c++1y and clang 3.5 with std=c++14 and std=c++1z
This code compiles:
#include <iostream>
template<class T>
class MyClass {
public:
MyClass(T const& a) : impl(a) {}
template<class T0, class T1> friend auto
// requires operator+(T0,T1) exists
operator+(MyClass<T0> const& a, MyClass<T1> const& b)
{
return MyClass<decltype(a.impl+b.impl)>{a.impl + b.impl};
}
T getImpl() const { return impl; }
private:
T impl;
};
int main() {
MyClass<int> x(2);
MyClass<long> y(2);
auto z = x+y;
std::cout << z.getImpl() << "\n";
}
Now if I define operator+ outside of the class, it does not compile anymore:
template<class T>
class MyClass {
public:
MyClass(T const& a) : impl(a) {}
template<class T0, class T1> friend auto
operator+(MyClass<T0> const& a, MyClass<T1> const& b);
T getImpl() const { return impl; }
private:
T impl;
};
template<class T0, class T1> auto
operator+(MyClass<T0> const& a, MyClass<T1> const& b)
{
return MyClass<decltype(a.impl+b.impl)>{a.impl + b.impl};
}
Clang 3.4 says:
error: use of overloaded operator '+' is ambiguous (with operand types MyClass<int> and MyClass<long>)
And then points at what it believes to be two different functions: the declaration in the class and the definition outside the class.
My question is: is it a clang bug, or just that template parameters are deduced for a friend function thus leading the two functions not being equivalent is some cases ?
And what alternative would you suggest: make operator+ a member function, or define friend operator+ inside the class (which would in my opinion clutter the class interface) ?
Just for your information, I have a real use case of such code, where I try to wrap a third -party matrix class and I need return type deduction because of the use of expression template for lazy evaluation.
Edit: The following does work (but still clutters the interface...)
template<typename T>
class MyClass
{
T impl;
public:
explicit MyClass(T a) : impl(std::move(a)) { }
T const& getImpl() const { return impl; }
template<typename T0, typename T1>
friend auto operator +(MyClass<T0> const& a, MyClass<T1> const& b) -> MyClass<decltype(a.impl + b.impl)>;
};
template<typename T0, typename T1>
auto operator +(MyClass<T0> const& a, MyClass<T1> const& b) -> MyClass<decltype(a.impl + b.impl)>
{
return MyClass<decltype(a.impl + b.impl)>(a.impl + b.impl);
}
edit:
look at the comments section, it's a bug in gcc 4.8.2 and 4.9
Gcc error code:
prog.cpp:10:61: error: non-static data member declared 'auto'
operator+(MyClass const& a, MyClass const& b)
^ prog.cpp: In function 'int main()': prog.cpp:25:15: error: no match
for 'operator+' (operand types are 'MyClass' and 'MyClass')
auto z = x+y;
^