C++ template operator+ overloading in inner class - c++

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

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.

C++ enable_if_t SFINAE

I am trying to understand why this piece is code isn't working as expected
#include <cstdio>
#include <vector>
#include <type_traits>
using namespace std;
struct Foo {
};
template<typename T, typename = void>
void compare(const T&a, const T&b) {
cout << "default" << endl;
}
template<typename T, std::enable_if_t<std::is_same<T, Foo>::value>>
void compare(const T& a, const T &b) {
cout << "In object" << endl;
}
int main(int argc, char const *argv[]) {
compare(1, 2);
{
vector<int> a, b;
compare(a, b);
}
{
Foo a, b;
compare(a, b);
}
return 0;
}
In all cases "default" is printed. For the last case I would expect that the 2nd function to get invoked.
You didn't specialize compare (it's impossible to partially specialize a function template anyway). Instead you provide an overload.
And the overload is always illegal:
Either enable_if_t is not defined.
Or it specifies a non-type template parameter of type void.
So it's never going to be called, because SFINAE discards it in favor of the always valid overload at the top.
Specialization is usually the wrong answer for function templates. Instead you should delegate to a class template, which will behave as expected upon true specialization:
template<typename T, typename = void>
struct compare_impl {
static void execute(T const& l, T const& r) { /*Base case code*/ }
};
template<typename T>
struct compare_impl<T, std::enable_if_t<std::is_same<T, Foo>::value>> {
static void execute(T const& l, T const& r) { /*Special case code*/ }
};
template<typename T>
void compare (T const& l, T const& r) { compare_impl<T>::execute(a, b); }
Using a class template is a valid solution, here's another way to achieve this result with tag dispatching:
template <class T>
void compare(const T & l, const T & r, std::true_type)
{
cout << "In object" << endl;
}
template <class T>
void compare(const T & l, const T & r, std::false_type)
{
cout << "default" << endl;
}
template <class T>
void compare(const T & l, const T & r)
{
compare(l, r, std::is_same<T, Foo>{});
}
Deferring to a specialised function object allows a lot of flexibility on how argument types and/or whether they are rvalues or lvalues.
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
// general case
template<class T>
struct compare_impl
{
template<class U, class V>
auto operator()(U&& a, V&& b) const {
cout << "default" << endl;
}
};
// compare interface
template<class T, class U>
auto compare(T && a, U && b)
{
using ctype = std::common_type_t<std::decay_t<T>, std::decay_t<U>>;
auto impl = compare_impl<ctype>();
return impl(a, b);
}
// now specialise for objects for which we want custom behaviour
struct Foo {
};
template<>
struct compare_impl<Foo>
{
template<class U, class V>
auto operator()(U&& a, V&& b) const {
cout << "In object" << endl;
}
};
int main(int argc, char const *argv[]) {
compare(1, 2);
{
vector<int> a, b;
compare(a, b);
}
{
Foo a, b;
compare(a, b);
}
return 0;
}

Specialize cast operator based on type traits

This is a follow-up to my previous question.
I have a class with a cast operator to anything. In a pre-C++17 environment this yields errors of being unable to select appropriate constructor overload while performing initialization. I want to tune the behavior by marking the cast operator explicit for some types. However, I cannot find a way to do so.
Here is an artificial example: I want an implicit cast operator to integer types and explicit to all other types.
This doesn't work because we cannot determine U having the expression of type typename std::enable_if<!std::is_integral<U>::value, U>::type:
struct C {
template<typename U>
operator typename std::enable_if< std::is_integral<U>::value, U>::type() const {
return 1;
}
template<typename U>
explicit operator typename std::enable_if<!std::is_integral<U>::value, U>::type() const {
return 1.5;
}
};
This one fails to compile saying that C::operator U() cannot be overloaded:
struct C {
template<typename U, typename = typename std::enable_if< std::is_integral<U>::value, U>::type>
operator U() const {
return 1;
}
template<typename U, typename = typename std::enable_if<!std::is_integral<U>::value, U>::type>
explicit operator U() const {
return 1.5;
}
};
I cannot declare the function of kind template<typename U, typename = void> operator U(); and partially specialize it because partial function specialization is not allowed and making a helper class looks like an overkill to me.
How can I declare cast operator based on some traits of the type I'm casting to?
I need a C++11 solution, as in C++17 the issue from my previous question is already resolved.b
You can move definitions of these operators to the base classes. This approach allows you put constraints on both implicit and explicit operators:
#include <type_traits>
#include <iostream>
template<typename TDerived> class
t_ImplicitlyConvertableToAnything
{
public: template
<
typename TTarget
, typename TEnabled = typename ::std::enable_if_t<::std::is_integral<TTarget>::value>
>
operator TTarget(void) const
{
auto const & self{static_cast<const TDerived &>(*this)};
return(self.template CheckedConversion_To_Integral<TTarget>());
}
};
template<typename TDerived> class
t_ExplicitlyConvertableToAnything
{
public: template
<
typename TTarget
, typename TEnabled = typename ::std::enable_if_t<!::std::is_integral<TTarget>::value>
> explicit
operator TTarget(void) const
{
auto const & self{static_cast<const TDerived &>(*this)};
return(self.template CheckedConversion_To_NonIntegral<TTarget>());
}
};
class
t_ConvertableToAnything
: public t_ImplicitlyConvertableToAnything<t_ConvertableToAnything>
, public t_ExplicitlyConvertableToAnything<t_ConvertableToAnything>
{
public: template<typename TTarget> decltype(auto)
CheckedConversion_To_Integral(void) const
{
return(static_cast<TTarget>(1));
}
public: template<typename TTarget> decltype(auto)
CheckedConversion_To_NonIntegral(void) const
{
return(static_cast<TTarget>(3.14));
}
};
int main()
{
t_ConvertableToAnything c;
::std::cout << ([](int x){return(x);})(c) << ::std::endl;
::std::cout << static_cast<float>(c) << ::std::endl;
return(0);
}
Run this code online
You can use non-type template parameters to avoid the "cannot be overloaded" issue:
#include <iostream>
#include <type_traits>
struct A { };
struct B { };
struct C {
template <typename U,
typename std::enable_if<std::is_integral<U>::value>::type* = nullptr>
explicit operator U() const {
return 1;
}
template<typename U,
typename std::enable_if<std::is_same<U, A>::value>::type* = nullptr>
explicit operator U() const {
return A{ };
}
template<typename U,
typename std::enable_if<std::is_same<U, B>::value>::type* = nullptr>
explicit operator U() const {
return B{ };
}
};
int main() {
C c;
long y = static_cast<int>(c);
B b = static_cast<B>(c);
A a = static_cast<A>(c);
}
https://ideone.com/smfPwF
You can overload your cast operator using a trick with dummy template parameters for disambiguation.
struct C {
template<typename U,
typename = typename enable_if<is_integral<U>::value, U>::type,
int = 0> // <== hete
operator U() const {
return 1;
}
template<typename U,
typename = typename enable_if<!is_integral<U>::value, U>::type,
char = 0> // <== and here
explicit operator U() const {
return 1.5;
}
};
Since the template signatures are now different, there is no ambiguity.
Try this. Just leave off the constraints on the explicit operator since it covers all cases that the first operator does not.
Coliru example: http://coliru.stacked-crooked.com/a/3d0bc6e59ece55cf
#include <iostream>
#include <type_traits>
struct C {
template <typename U,
typename = typename std::enable_if< std::is_integral<U>::value>::type>
operator U() const {
return 1;
}
template<typename U, typename std::enable_if<!std::is_integral<U>::value>::type* = nullptr>
explicit operator U() const {
return 1.5;
}
};
int main() {
C c;
int v = c;
int w = c;
int x = static_cast<int>(c);
long y = static_cast<int>(c);
double z = static_cast<double>(c);
std::cout << v << std::endl;
std::cout << w << std::endl;
std::cout << x << std::endl;
std::cout << y << std::endl;
std::cout << z << std::endl;
}
Thanks to #Jodocus for enabling explicit casts to integral types.

Template conversion operator returing reference to array

I was reading this question and some other stuff : Are there cases where a typedef is absolutely necessary??
I wrote this code :
const int arrayOfInt[10] = {0};
template<typename T, int N> using X = const T (&)[N];
struct foo
{
template<typename T, int N> operator X<int,10> () { return arrayOfInt; }
};
void bar(const int (&) [10]) {}
int main()
{
bar(foo());
return 0;
}
using feature of c++11 is not working for me , also I'm unable to think how to typedef the return type in this case too since my class foo is not template itself. I need to see solution using using keyword and typedef both . Thanks a lot awesome peoples of SO :)
Since X is an alias template you need to provide the template arguments explicitly; they won't be captured from the surrounding scope:
struct foo
{
template<typename T, int N>
operator X<T,N>() { return arrayOfInt; }
// ^^^^^
};
You can't do this with a typedef as there's no such thing as a typedef template.
template<typename T, int N> operator X<int,10> () { return arrayOfInt; }
template arguments T and N are never used and hence shall never be deduced.
Fixed Live On Coliru
const int arrayOfInt[10]{0};
template<typename T, int N> using X = T const (&)[N];
struct foo {
template<typename T, int N> operator X<T,N> () const { return arrayOfInt; }
};
void bar(const int (&) [10]) {}
int main()
{
foo f;
X<int, 10> v = f;
bar(foo());
}

C++ template automatic type promotion

template <class T>
class Foo {
public:
T val;
Foo(T _v): val(_v){}
friend ostream& operator<< (ostream& out, const Foo& A) {
out << A.val;
return out;
}
};
template <class X, class Y> Foo<???> operator+(const Foo<X>& A, const Foo<Y> & B) {
if (sizeof(X) > sizeof (Y))
return Foo<X>(A.val + B.val);
else
return Foo<Y>(A.val + B.val);
}
int main() {
Foo<double> a(1.5);
Foo<int> b(2);
cout << a << endl;
cout << b << endl;
cout << a+b << endl;
}
My goal is to have the operator+ function to return different type based on types of the arguments.
For example, if a is int and b is int then return Foo<int>, if one or both of them are double then return Foo<double>.
Is it possible to do?
(C++11): Use a declval expression inside dectype:
#include <utility>
template <class X, class Y>
Foo<decltype(std::declval<X>() + std::declval<Y>())> operator+(...);
This is possible (in C++03 or C++11) using partial template specializations.
// C++ does not allow partial specialization of function templates,
// so we're using a class template here.
template <typename X, typename Y, bool xLarger>
struct DoPlusImpl // When x is selected
{
typedef Foo<X> result_type;
};
template <typename X, typename Y>
struct DoPlusImpl<X, Y, false> // When y is selected
{
typedef Foo<Y> result_type;
};
template <typename X, typename Y> // Select X or Y based on their size.
struct DoPlus : public DoPlusImpl<X, Y, (sizeof (X) > sizeof (Y))>
{};
// Use template metafunction "DoPlus" to figure out what the type should be.
// (Note no runtime check of sizes, even in nonoptimized builds!)
template <class X, class Y>
typename DoPlus<X, Y>::result_type operator+(const Foo<X>& A, const Foo<Y> & B) {
return typename DoPlus<X, Y>::result_type
(A.val + B.val);
}
You can see this in action on IDEOne here -> http://ideone.com/5YE3dg
Yes ! Here is the way if you want to give your own rules:
template <typename X, typename Y> struct Rule {};
template<> struct Rule<int, int> { typedef int type;};
template<> struct Rule<float, int> { typedef bool type;};
Then
template <class X, class Y> Foo<typename Rule<X, Y>::type> operator+(...)