0

In the following code, I wanted to achieve the effect of binary operator chaining for the specific type definition I want to use - for trivial operator chaining, that a binary operator returns the same type of object, most of cases just simply returning *this, which could trivially be used again to chain the next object of the same type.

However, in my case, the binary operators takes two reference_wrappers of two same typed objects (awaitable<T>::ref) as input, and returns an aggregated object of type (awaitable<awaitable<T>::ref>), and I wanted to use the returned aggregate object to chain the next awaitable<T>::ref and again return the further aggregated object of type awaitable<awaitable<T>::ref> - notice that the returned object is always the same type of awaitable<awaitable<T>::ref>, no matter how many more chaining happens.

The friend operator with template template parameter defined at location (XXX) hoped to serve this purpose, but compilers don't seem to be willing to perform the bind.

Could anyone shed some lights on how I can achieve the result as described?

Thanks!

#include <functional>

template <typename T>
struct awaitable
{
    typedef std::reference_wrapper<awaitable> ref;

    // (A) - okay
    friend awaitable<ref> operator||(ref a1, ref a2)
    {
        awaitable<ref> r;
        return r;
    }

    // (XXX) - this doesn't bind
    template < template <typename> class _awaitable >
    friend awaitable<ref> operator||(typename awaitable<typename _awaitable<T>::ref>::ref a1, ref a2)
    {
        awaitable<ref> r;
        return r;
    }
};

int main(int argc, const char * argv[])
{
    awaitable<void> a1;
    awaitable<void> a2;
    auto r1 = a1 || a2; // Okay - r1 is of type awaitable<awaitable<void>::ref>

    awaitable<void> a3;
    auto r3 = r1 || a3; // doesn't bind to the operator defined at XXX

    return 0;
}

[EDIT] -

Answers in this post and this seem to explain the situation pretty nicely, but in my case, the friend operator has a template template parameter (which is needed to avoid recursive template instantiation), which might prevent the compilers to generate the correct namespace scope function when the template is instantiated?

Community
  • 1
  • 1
Dejavu
  • 1,299
  • 14
  • 24
  • Sorry for the edits, I tried to simplify the code and eliminate any possible confusions as much as possible, and hopefully the code above should be very simple to read (no down scrolling at all!) – Dejavu Jan 11 '17 at 20:44
  • Note: the input objects are all reference_wrapper wrapped, I specifically don't want copy happening, and in the real code, the copy constructor is deleted – Dejavu Jan 11 '17 at 21:48

2 Answers2

0

is this what you need?

template < template <typename> class _awaitable, typename U >
friend auto operator||(_awaitable<std::reference_wrapper<U>> a1, ref a2)
{
    awaitable<ref> r;
    return r;
}

live demo

EDIT1

I saw your answer where you removed the template parameter to get it working. That works great if void is the only the type you use. If you try to use another type it will fail though. The closest I've got to get around it is explicity using std::ref(r1) e.g.

template<typename U>
friend awaitable<ref> operator||(std::reference_wrapper<awaitable<std::reference_wrapper<awaitable<U>>>> a1, ref a2)
{
    std::cout << "(XXX2)" << std::endl;
    awaitable<ref> r;
    return r;
}

awaitable<int> a4;
auto r4 = std::ref(r1) || a4; 

live demo 2

Biggy Smith
  • 910
  • 7
  • 14
  • No. You are missing a ::ref after _awaitable, see this: http://coliru.stacked-crooked.com/a/ed848c7f0098741d – Dejavu Jan 11 '17 at 21:29
  • but thats not what you are or'ing, you are calling r1 || a3 -> awaitable > > || awaitable – Biggy Smith Jan 11 '17 at 21:42
  • I want the object to be implicitly converted to the ref version, which the first definition worked, and I'm wondering why XXX doesn't – Dejavu Jan 11 '17 at 21:43
  • 1
    ah I believe we are running into the implicit template overload resolution issue [discussion here](https://stackoverflow.com/questions/9787593/c-implicit-type-conversion-with-template), not quite sure how to fix – Biggy Smith Jan 11 '17 at 22:34
  • That's exactly why (A) worked. But (XXX) remains not working :-( – Dejavu Jan 11 '17 at 22:43
  • Based on the linked answer, it seems that a friend function with template template parameter doesn't get instantiated as a namespace free function, and thus cannot be used for overloading resolution? – Dejavu Jan 11 '17 at 23:04
  • I do think thats the issue. I've edited my answer with a workaround for other types, although what you have changed my be enough... – Biggy Smith Jan 12 '17 at 14:29
  • My change should work for other types, see: http://coliru.stacked-crooked.com/a/2500822b2e3a65ff – Dejavu Jan 12 '17 at 18:50
  • I meant different types on either side of the || e.g. writable || writable, but you may not be needing that if you are sticking to one templated type. – Biggy Smith Jan 12 '17 at 19:17
  • Yep, I needed the innermost type T to be consistent. – Dejavu Jan 12 '17 at 20:26
0

It seems that the friend function with template template parameter makes template deduction fail. The solution is to remove the template template parameter, and expand the ::ref usage to std::reference_wrapper within the friend function definition:

#include <functional>

template <typename T>
struct awaitable
{
    typedef std::reference_wrapper<awaitable> ref;

    // (A) - okay
    friend awaitable<ref> operator||(ref a1, ref a2)
    {
        awaitable<ref> r;
        return r;
    }

    // (XXX) - removing the template template parameter makes the template instantiation for a specific type T to generate a namespace version of the function!
    friend awaitable<ref> operator||(std::reference_wrapper<awaitable<std::reference_wrapper<awaitable<T>>>> a1, ref a2)
    {
        awaitable<ref> r;
        return r;
    }
};

// template <typename T>
// awaitable<typename awaitable<T>::ref> operator||(typename awaitable<typename awaitable<T>::ref>::ref a1, typename awaitable<T>::ref a2)
// {
//     awaitable<typename awaitable<T>::ref> r;
//     return r;
// }

int main(int argc, const char * argv[])
{
    awaitable<void> a1;
    awaitable<void> a2;
    auto r1 = a1 || a2; // Okay - r1 is of type awaitable<awaitable<void>::ref>

    awaitable<void> a3;
    auto r3 = r1 || a3; // now it works!

    return 0;
}

Demo here

Dejavu
  • 1,299
  • 14
  • 24