具有模板模板参数重载的 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;
}
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;
好像是友元函数带模板模板参数导致模板推导失败。解决方法是去掉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
在下面的代码中,我想为我想使用的特定类型定义实现二元运算符链接的效果——对于简单的运算符链接,二元运算符 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;
}
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;
好像是友元函数带模板模板参数导致模板推导失败。解决方法是去掉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