Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 523
Miguel Avatar asked Oct 30 '22 08:10

Miguel


1 Answers

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;
    }
};
like image 111
Barry Avatar answered Nov 12 '22 22:11

Barry