具有模板模板参数重载的 C++11/14 友元运算符

C++11/14 friend operator with template template parameter overloading

在下面的代码中,我想为我想使用的特定类型定义实现二元运算符链接的效果——对于简单的运算符链接,二元运算符 return 是同一类型的对象,大多数情况只是简单地 returning *this,它可以再次用于链接相同类型的下一个对象。

但是,在我的例子中,二元运算符将两个相同类型的对象 (awaitable<T>::ref) 中的两个 reference_wrappers 作为输入,returns 是一个聚合类型的对象 (awaitable<awaitable<T>::ref>),我想使用 returned 聚合对象链接下一个 awaitable<T>::ref 并再次 return 类型 awaitable<awaitable<T>::ref> 的进一步聚合对象 - 请注意returned 对象始终是 awaitable<awaitable<T>::ref> 的同一类型,无论发生了多少链接。

在位置 (XXX) 处定义模板模板参数的友元运算符希望达到此目的,但编译器似乎不愿意执行绑定。

任何人都可以阐明我如何才能达到所描述的结果吗?

谢谢!

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

[编辑] -

this post and this 中的答案似乎很好地解释了这种情况,但在我的例子中,友元运算符有一个模板模板参数(这是避免递归模板实例化所必需的),这可能会阻止编译器实例化模板时生成正确的命名空间作用域函数?

这是你需要的吗?

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

我看到了您的回答,您删除了模板参数以使其正常工作。如果 void 是您使用的唯一类型,那效果很好。如果您尝试使用其他类型,它将失败。我必须绕过它的最接近的方法是明确使用 std::ref(r1) 例如

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

好像是友元函数带模板模板参数导致模板推导失败。解决方法是去掉template模板参数,在友元函数定义中将::ref用法扩展为std::reference_wrapper:

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

演示 here