【问题标题】:C++11/14 friend operator with template template parameter overloading具有模板模板参数重载的 C++11/14 友元运算符
【发布时间】:2017-01-11 20:20:20
【问题描述】:

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

但是,在我的例子中,二元运算符将两个相同类型对象 (awaitable<T>::ref) 的两个 reference_wrappers 作为输入,并返回一个类型为 (awaitable<awaitable<T>::ref>) 的聚合对象,我想使用返回的聚合对象链接下一个 awaitable<T>::ref 并再次返回 awaitable<awaitable<T>::ref> 类型的进一步聚合对象 - 请注意,无论发生多少链接,返回的对象始终是 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 帖子和this 中的答案似乎很好地解释了这种情况,但在我的情况下,朋友运算符有一个模板模板参数(需要它来避免递归模板实例化),这可能会阻止编译器模板实例化时生成正确的命名空间作用域函数?

【问题讨论】:

  • 抱歉编辑,我试图简化代码并尽可能消除任何可能的混淆,希望上面的代码应该非常易于阅读(根本不需要向下滚动!)跨度>
  • 注意:输入的对象都是reference_wrapper包装的,我特别不希望发生复制,而在实际代码中,复制构造函数被删除了

标签: c++11 templates operator-overloading c++14 overload-resolution


【解决方案1】:

似乎是带有模板模板参数的友元函数导致模板推导失败。解决的办法是去掉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

【讨论】:

    【解决方案2】:

    这是你需要的吗?

    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

    【讨论】:

    • 没有。您在 _awaitable 之后缺少 ::ref,请参见:coliru.stacked-crooked.com/a/ed848c7f0098741d
    • 但那不是你正在做的,你正在调用 r1 || a3 -> 可等待<:reference_wrapper> > > ||等待
    • 我希望将对象隐式转换为第一个定义有效的 ref 版本,我想知道为什么 XXX 没有
    • 啊我相信我们遇到了隐式模板重载解决问题discussion here,不太确定如何解决
    • 这正是 (A) 起作用的原因。但是(XXX)仍然无法正常工作:-(
    猜你喜欢
    • 2011-04-28
    • 2016-04-14
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 2021-05-18
    • 2011-06-07
    • 2010-11-20
    相关资源
    最近更新 更多