Operator overloading on an enum nested in a class - c++

The problem
Given the following piece of code :
template <typename T>
struct dummy {
enum enumenum { a = 1, b = 2, c = 4 };
};
int main() {
// I want this line to expands as :
// dummy<double>::enumenum a = operator~(dummy<double>::a);
auto a = ~dummy<double>::a;
}
How do you overload operators on enumenum ?
I'm using std C++14.
What I tried
A naive implementation:
template <typename T>
typename dummy<T>::enumenum
operator~(typename dummy<T>::enumenum a) {
return static_cast<typename dummy<T>::enumenum>(operator~(a));
}
Unfortunately the line in question expands as:
int a = ~static_cast<int>(dummy<double>::a);
Which means that the operator was not used (this is the default behavior).
Is it because ADL could not find the right operator~() in the struct namespace (is that even a thing ?) ?
Then I tried: (note the friend)
template <typename T>
struct dummy {
enum enumenum { a, b, c };
friend enumenum operator~(enumenum a) {
return static_cast<enumenum>(~a);
}
};
This actually works and expands as:
template <>
struct dummy<double> {
enum enumenum {
a = static_cast<unsigned int>(1),
b = static_cast<unsigned int>(2),
c = static_cast<unsigned int>(4)
};
friend inline dummy<double>::enumenum operator~(dummy<double>::enumenum a) {
return static_cast<dummy<double>::enumenum>(operator~(a));
}
};
int main()
{
dummy<double>::enumenum a = operator~(dummy<double>::a);
}
This is the behavior I want. Except, what if I don't want to define the operator in the class body.
So I tried :
template <typename T>
struct dummy {
enum enumenum { a = 1, b = 2, c = 4 };
// if inline : inline function 'operator~' is not defined [-Wundefined-inline]
// and adding inline to the template below does not help
friend enumenum operator~(enumenum a);
};
template <typename T>
typename dummy<T>::enumenum
operator~(typename dummy<T>::enumenum a) {
return static_cast<typename dummy<T>::enumenum>(~a);
}
int main() {
auto a = ~dummy<double>::a;
}
The code above expands as:
template<>
struct dummy<double>
{
enum enumenum
{
a = static_cast<unsigned int>(1),
b = static_cast<unsigned int>(2),
c = static_cast<unsigned int>(4)
};
friend dummy<double>::enumenum operator~(dummy<double>::enumenum a);
};
int main()
{
dummy<double>::enumenum a = operator~(dummy<double>::a);
}
This compiles, but does not link!
Edit: I believe it does not link because the template is not instantiated thus failing at link time (similarly to the naive implementation above).
Conclusion
Even though I somehow found a way to achieve what I wanted, what if I don't want to define the operator inside the class definition.
Thanks in advance.

This compiles, but does not link!
Compile but doesn't link because you declare a non-template operator (it's inside a template struct but isn't a template function)
friend enumenum operator~(enumenum a);
and you define a template one
template <typename T>
typename dummy<T>::enumenum
operator~(typename dummy<T>::enumenum a) {
return static_cast<typename dummy<T>::enumenum>(~a);
}
and a template definition can't match a non-template declaration.
You could try to declare the function as a template one
template <typename T>
struct dummy
{
enum enumenum { a = 1, b = 2, c = 4 };
template <typename U>
friend typename dummy<U>::enumenum
operator~ (typename dummy<U>::enumenum const & a);
};
template <typename T>
typename dummy<T>::enumenum
operator~ (typename dummy<T>::enumenum const & a)
{ return static_cast<typename dummy<T>::enumenum>(~a); }
int main ()
{
auto a = ~dummy<double>::a;
}
but, unfortunately, this compile but, calling the ~ operator as follows
~dummy<double>::a;
isn't called the template function because the template parameter T can't be deduced because is in not-deduced-context (before the last ::), as pointed by Benny K in a comment.
So, instead the template operator~(), the dummy<double>::a is converted to int and the ~ operator for int is applied (with type result int).
You can verify this point explicitly calling a function operator~()
auto a = operator~(dummy<double>::a);
You should get a "no matching function for call to 'operator~'" error (with note "note: candidate template ignored: couldn't infer template argument 'T'") or something similar.
To make this solution works, you have to explicate the type of the class, to avoid the template deduction
auto a = operator~<double>(dummy<double>::a);
and, now, you can verify that a is a dummy<double>::enumenum
static_assert( std::is_same<decltype(a), dummy<double>::enumenum>::value, "!" );
But, obviously, this isn't a satisfactory solution (and very dangerous, if you forget to avoid the simple use of ~).
Otherwise you can define the operator as non-template
template <typename T>
struct dummy
{
enum enumenum { a = 1, b = 2, c = 4 };
friend enumenum operator~ (enumenum const & a);
};
dummy<double>::enumenum
operator~(dummy<double>::enumenum const & a)
{ return static_cast<dummy<double>::enumenum>(~a); }
int main ()
{
auto a = ~dummy<double>::a;
}
but you have to define a different operator for every dummy<T> type.
IMHO the most simple, safe and elegant solution is your working one: declare/define the operator inside the struct.

Related

Operator overloading for nested struct only working as member or friend function

This C++ code compiles and runs perfectly, as I expect:
template <typename T> struct S { T *p; };
template <typename T>
bool operator == (S<T> &a, S<T> &b) { return a.p == b.p; }
int main () { int i; S<int> a = {&i}, b = {&i}; return a == b; }
However, if I try to do the same with the inner struct of an outer struct...
template <typename T> struct O { struct I {T *p;}; };
template <typename T>
bool operator == (O<T>::I &a, O<T>::I &b) { return a.p == b.p; }
int main () { int i; O<int>::I a = {&i}, b = {&i}; return a == b; }
... then it doesn't compile anymore (gcc version 8.3.0, Debian GNU/Linux 10):
1.cpp:4:25: error: declaration of ‘operator==’ as non-function
bool operator == (O<T>::I &a, O<T>::I &b) { return a.p == b.p; }
^
[...]
Why is it so? I also do not understand the above error message.
Note that I'm aware that I can make it work by defining the operator as a member function of the inner struct:
template <typename T>
struct O2 {
struct I2 {
T *p;
bool operator == (I2 &b) { return p == b.p; }
};
};
int main () { int i; O2<int>::I2 a = {&i}, b = {&i}; return a == b; }
However, if somehow possible, I'd rather use the non-member function version, because I find it more symmetric and therefore clearer.
Also, partly by trial and error, I found that the following symmetric version works...
template <typename T>
struct O3 {
struct I3 { T *p; };
friend bool operator == (I3 &a, I3 &b) { return a.p == b.p; }
};
int main () { int i; O3<int>::I3 a = {&i}, b = {&i}; return a == b; }
... but I do not really understand what is happening above. First, given that a friend declaration "grants a function or another class access to private and protected members of the class where the friend declaration appears", I do not understand how it helps in the code above, given that we're always dealing with structs and therefore with public members.
Second, if I remove the friend specifier, then it doesn't compile anymore. Also, the [...] operator== [...] must have exactly one argument error message makes me think that in this case the compiler expects me to define a member function operator== whose left operand is O3, not I3. Apparently, however, the friend specifier changes this situation; why is it so?
First, the compiler gets confused by missing typename. The error message really is confusing and can be silenced via:
template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) {
return a.p == b.p;
}
Now the actual problem gets more apparent:
int main () {
int i;
O<int>::I a = {&i}, b = {&i};
return a == b;
}
results in the error:
<source>: In function 'int main()':
<source>:15:14: error: no match for 'operator==' (operand types are 'O<int>::I' and 'O<int>::I')
15 | return a == b;
| ~ ^~ ~
| | |
| | I<[...]>
| I<[...]>
<source>:8:6: note: candidate: 'template<class T> bool operator==(typename O<T>::I&, typename O<T>::I&)'
8 | bool operator == (typename O<T>::I &a, typename O<T>::I &b) {
| ^~~~~~~~
<source>:8:6: note: template argument deduction/substitution failed:
<source>:15:17: note: couldn't deduce template parameter 'T'
15 | return a == b;
| ^
It is not possible to deduce T from a == b, (#dfribs words)
because T is in a non-deduced context in both of the function
parameters of the operator function template; T cannot be deduced
from O<T>::I&, meaning T cannot be deduced from any of the
arguments to the call (and function template argument deduction
subsequently fails).
Sloppy speaking, because O<S>::I could be the same as O<T>::I, even if S != T.
When the operator is declared as member then there is only one candidate to compare a O<T>::I with another (because the operator itself is not a template, ie no deduction needed).
If you want to implement the operator as non member I would suggest to not define I inside O:
template <typename T>
struct I_impl {
T *p;
};
template <typename T>
bool operator == (I_impl<T> &a,I_impl<T> &b) {
return a.p == b.p;
}
template <typename T>
struct O {
using I = I_impl<T>;
};
int main () {
int i;
O<int>::I a = {&i}, b = {&i};
return a == b;
}
Your confusion about friend is somewhat unrelated to operator overloading. Consider:
#include <iostream>
void bar();
struct foo {
friend void bar(){ std::cout << "1";}
void bar(){ std::cout << "2";}
};
int main () {
bar();
foo{}.bar();
}
Output:
12
We have two definitions for a bar in foo. friend void bar(){ std::cout << "1";} declares the free function ::bar (already declared in global scope) as a friend of foo and defines it. void bar(){ std::cout << "2";} declares (and defines) a member of foo called bar: foo::bar.
Back to operator==, consider that a == b is a shorther way of writing either
a.operator==(b); // member ==
or
operator==(a,b); // non member ==
Member methods get the this pointer as implicit parameter passed, free functions not. Thats why operator== must take exactly one parameter as member and exactly two as free function and this is wrong:
struct wrong {
bool operator==( wrong a, wrong b);
};
while this is correct:
struct correct {
bool operator==(wrong a);
};
struct correct_friend {
friend operator==(wrong a,wrong b);
};
Compiling C++ does not require compilers to solve the halting problem.
template <typename T> struct O { struct I {T *p;}; };
template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) { return a.p == b.p; }
going from O<int>::I to int requires, in the general case, that the compiler solve the halting problem. To demonstrate why:
template <typename T> struct O { struct I {T *p;}; };
template <> struct O<double> { using I=O<int>::I; };
template <> struct O<char> { using I=std::string; };
template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) { return a.p == b.p; }
now, O<int>::I can be named O<double>::I. You can actually make the map from O<T>::I to T require inverting an arbitrary turing-complete function, as template specialization is turing-complete.
Rather than carving out a region of invertable dependent type maps, C++ simply says "do not invert dependent types" when doing template pattern matching of arguments.
So
template <typename T>
bool operator == (typename O<T>::I &a, typename O<T>::I &b) { return a.p == b.p; }
will never deduce T.
Now you can make this work, but it requires that you define the inverse mapping without relying on template type deduction.
By far the easiest is to make I be defined outside O. Failing that, you need to define a way to find O<T> given O<T>::I, like:
template <typename T> struct O {
struct I {using Outer=O<T>;T *p;};
};
template<class Inner>
using Outer=typename Inner::Outer;
template<class X>
struct Type0;
template<template<class...>class Z, class T0, class...Ts>
struct Type0<Z<T0,Ts...>>{ using type=T0; };
template<class X>
using Type0_t=typename Type0<X>::type;
we can then
template <class I> requires (std::is_same_v<I, typename O<Type0_t<Outer<Inner>>>::I>)
bool operator == (Inner const &a, Inner const &b) { return a.p == b.p; }
and your code works.

Splatting a struct

I have a bunch of structs like this with increasing number of members, but consistent member naming:
struct one { int a; };
struct two { int a; int b; };
struct three { int a; int b; int c; };
I also have a templated function which I want to have accept one of these struct's members, splatted:
template <typename T, typename ... ARGS> // T will be one, two, or three
void func(ARGS... args); // This should take 1, 2, or 3, int arguments respectively
I want to be able to call this something like:
two foo;
func<two>(splatter(foo));
Where splatter would somehow split foo so that it would resolve to func<two>(foo.a, foo.b).
I can obviously just expand this inline, without splatter, but the code in which I call func is itself happily templated. I've tried using an initializer_list but I can't figure out how to build one based on template type alone.
Unfortunately my compiler also doesn't support constexpr if to splat a call to func or build an initializer_list. Are there any other options available to me?
As far as I know, what you describe cannot be done using c++. Or if it can, than it is a very complicated solution. The reason is, that you would need to somehow store pointers to class member access functions and then call them properly with your actual object.
However, you get a similar functionality with overloading, which is much easier to implement. For example, you could define a call_func which you overload for your types:
#include <array>
// types
struct one {
int a;
};
struct two {
int a;
int b;
};
struct three {
int a;
int b;
int c;
};
template <class T>
struct more_complex_type {
T a;
T b;
};
// template function
template <typename T, typename... ARGS>
auto func(ARGS... args) {
return std::array<T, sizeof...(args)>{args...};
}
// indirection overload
template <class T>
struct call_func_impl {};
template <>
struct call_func_impl<one> {
auto call(one val) { return func<int>(val.a); }
};
template <>
struct call_func_impl<two> {
auto call(two val) { return func<int>(val.a, val.b); };
};
template <>
struct call_func_impl<three> {
auto call(three val) { return func<int>(val.a, val.b, val.c); };
};
template <class T>
struct call_func_impl<more_complex_type<T>> {
auto call(more_complex_type<T> val) { return func<T>(val.a, val.b); };
};
// syntacting sugar
template <class T>
auto call_func(T val) {
return call_func_impl<T>{}.call(val);
}
// tests
auto test_func() { return func<int>(1, 2, 3, 4, 5); }
auto test_func_of_one() {
auto val = one{};
return call_func(val);
}
auto test_func_of_two() {
auto val = two{};
return call_func(val);
}
auto test_func_of_three() {
auto val = three{};
return call_func(val);
}
auto test_func_of_more_complex_type() {
auto val = more_complex_type<double>{};
return call_func(val);
}
This example uses an overloaded struct template to wrap the function call. This might not be necessary for your case, as you do not have templatized types. You actually could just overload call_func. However, this approach allows you to define the call for more_complex_type which is templatized, as partial function overloading is currently not possible in c++.

C++ template operator+ overloading in inner class

How do I overload operator+ for inner class of a class template? I've searched for hours now and I can't find an answer. This is a minimal example that does not work:
#include <iostream>
using namespace std;
template <class T>
struct A
{
struct B
{
T m_t;
B(T t) : m_t(t) {}
};
};
template <class T>
typename A<T>::B operator+(typename A<T>::B lhs, int n)
{
lhs.m_t += n;
return lhs;
}
int main(int argc, char **argv)
{
A<float> a;
A<float>::B b(17.2);
auto c = b + 5;
cout << c.m_t << endl;
return 0;
}
If I compile like this, I get error: no match for ‘operator+’ (operand types are ‘A<float>::B’ and ‘int’)
I found somewhere that operator+(A<T>::B, int) should be declared, so if I add the following:
struct B;
friend B operator+(typename A<T>::B lhs, int n);
after struct A {, I get a linker error.
If I don't try to call b+5, the program compiles correctly.
How did they (STL makers) program vector<T>::iterator operator+ with an int? I can't find it anywhere (and it's kind of hard to read stl_vector.h)!
Thank you.
The problem you're facing is that when you declare a function template like:
template <class T>
typename A<T>::B operator+(typename A<T>::B lhs, int n)
typename A<T>::B lhs is a non-deduced context. There is no way for the compiler to determine what T is in that context, so it doesn't try, so it cannot find your operator+. Consider a reduced example like:
template <class T> void foo(typename T::type );
struct A { using type = int; };
struct B { using type = int; };
foo(0); // what would T be?
// how many other possible T's are there that fit?
In order for template deduction to succeed with non-deduced contexts, the template type parameter must be explicitly specified. In this case, this monstrosity of a syntax compiles:
auto c = ::operator+<float>(b, 5);
But probably isn't your intended usage!
You will need to declare the operator+ within struct B:
struct B
{
T m_t;
B(T t) : m_t(t) {}
// member
B operator+(int n) {
return B(m_t + n);
}
// or non-member, non-template friend
friend B operator+(B lhs, int n) {
lhs.m_t += n;
return lhs;
}
};
Maybe you could do something like this:
#include <iostream>
#include <type_traits>
using namespace std;
template <class T>
struct A
{
struct B
{
typedef A<T> OuterType;
T m_t;
B(T t) : m_t(t) {}
};
};
template <class T>
typename T::OuterType::B operator+(T lhs, int n)
{
lhs.m_t += n;
return lhs;
}
int main(int argc, char **argv)
{
A<float> a;
A<float>::B b(17.2);
auto c = b + 5;
cout << c.m_t << endl;
return 0;
}
Edit: This would work as the T is deducable from first operand of expression (b + 5) and works only for the structs that contains OuterType struct defined that has subtype B as an inner struct. You can test if this struct is the same as T using typename enable_if<is_same<T, typename T::OuterType::B>::value, T>::type instead of result type: typename T::OuterType::B

How to make a C++ templated function agnostic to the return type for future specialization

I would like to have a general templated function declaration for which I do not know (already) the return type, similar to:
**template <class T> auto getIds() noexcept -> std::vector<Any>;**
The function could then be specialized with several input types, and a return type based on it:
template <> auto getIds<MyClass>() noexcept -> std::vector<decltype(MyClass::id)>
{
// implementation here.
}
And finally call it without to set the return:
auto val = getIds<MyClass>();
Is that possible? How?
Notes:
What I want to avoid is to have to set manually the Id type in the call function:
auto val = getIds<MyClass, decltype(MyClass::id)>(); // Ugly
I also discard any (non based on template) solution like extending all types from a RootMyClass. Is not that these solutions are bad, but they miss the point of this question.
Trying to be a bit clearer:
If I wrote
class MyClass { public: int id1=4;};
template <class T, class Id> auto getIds() -> Id;
template <> auto getIds<MyClass, decltype(MyClass::id1)>() -> decltype(MyClass::id1)
{
return 1;
}
auto main() -> int
{
getIds<MyClass>(); // Do not compile
getIds<MyClass, decltype(MyClass::id1)>(); // Compile but ugly
}
I would like the return type to be implicit, but I did not found a way to achieve that with specializations:
template <class T> getIds() noexcept -> WHICH TYPE?;
You cannot change the return type in a specialization, unfortunately. What you can do is change the return type in different overloads. Obviously. Furthermore, function template specializations are much more complicated than function overloads anyway, so let's do that.
Introduce an empty type wrapper, say:
template <typename T> struct wrapper { };
And forward the default implementation to that (I'm assuming C++14 here, otherwise you could wrap that in decltype() with a trailing return):
template <typename T>
auto getIds() { return getIds(wrapper<T>{}); }
Declare the generic version as:
template <typename T>
void getIds(wrapper<T> );
Don't define it. Then, anytime somebody tries to do:
auto ids = getIds<X>();
If there is no overload, that will simply fail to compile as you cannot assign from void. Then, you can overload as you see fit:
std::vector<decltype(MyClass::id)> getIds(wrapper<MyClass> )
{ ... }
FINAL EXAMPLE:
#include <iostream>
#include <vector>
template <typename T> struct wrapper { };
template <typename T>
auto getIds() -> decltype(getIds(wrapper<T>{}))
{
return getIds(wrapper<T>{});
}
template <typename T>
void getIds(wrapper<T> ) { }
struct MyClass {
int id;
};
std::vector<decltype(MyClass::id)> getIds(wrapper<MyClass> )
{
return {1, 2, 3};
}
int main()
{
for (auto id : getIds<MyClass>()) {
std::cout << id << " ";
}
}
This is actually very similar to Haskell typeclasses, and, surprisingly, works. For real usage I would use functors to allow partial specializations, though.
#include <iostream>
template<typename T>
decltype(T::x) getX(T const& t) { return; }
class A { public: int x; A(int x):x(x){} };
template<> int getX<A>(A const& a) {
return a.x;
}
class B { public: std::string x; B(std::string x):x(std::move(x)){} };
template<> std::string getX<B>(B const& b) {
return b.x;
}
int main() {
A a(42);
B b("43");
std::cout << getX(a) << std::endl;
std::cout << getX(b) << std::endl;
}
As you can see, each specialization has to (can?) provide the return type explicitly. decltype(A::x) (and B::x), respectively) could be used instead if you so prefer.
To make it even more Haskell-ish, you could expect a type tag in the type itself (basically a type family):
template<typename T>
typename T::TypeOfX getX(T const& t) { return; }
And consequently:
class A {
using TypeOfX = int;
TypeOfX someComplexLogicToGetX();
};
Both solutions to the type being instantiated for the actual type, except one gets it from a type of a field, and the other from a direct "type variable".

Class template argument dependent on constructor

With a templated number wrapping struct:
template <int I> struct Num { static const int n = I; };
and a few overloaded functions:
template <typename T>
Num<0> id(T x) { return Num<0>(); }
Num<1> id(int x) { return Num<1>(); }
Num<2> id(double x) { return Num<2>(); }
Num<3> id(char x) { return Num<3>(); }
I can initialise the m_i member of a Zod struct using decltype and the type of the return argument of id:
template <typename T>
struct Zod {
Zod(T x) { m_i = identity<decltype(id(x))>::type::n; }
int m_i;
};
However, what I'd really like is for the Zod struct to have a second integer template argument initialised to the value which m_i was set to.
template <typename T, int I = ?>
struct Zod { ... }
This seems possible, as the identity/decltype expression evaluates to a compile time constant; for example, this is fine at global scope:
char c;
static const int g = identity<decltype(id(c))>::type::n;
The problem is that the x argument of the constructor is not available in the scope of Zod's template declaration. Can it be done?
It's perfectly possible- just pass in *((T*)nullptr) to obtain an lvalue of any type T regardless of it's constructability. After all, all you actually do with the constructor argument is pass it to id and then decltype that, which is perfectly doable in the template, since you know that the type of x is T.
template<typename T, int I = identity<decltype(id(*((T*)nullptr)))>::type::n> struct Zod {
...
};