当模板化的函数部分依赖于类参数和自定义模板参数时,声明它为友元



作为我最后一个问题的延续,我现在的情况是,我想声明一个函数作为依赖于类模板参数(R)和附加模板参数(Cb)的混合的朋友。如何在类上下文中正确地指定它?我天真的方法似乎不起作用:

演示
#include <memory>
#include <future>
#include <cstdio>
template <typename Fn, typename R, typename... Args>
concept invocable_r = std::is_invocable_r<R, Fn, Args...>::value;
/* header.hpp */
template <typename T>
class entity;
template <typename R, invocable_r<R> Cb>
auto create_entity(Cb&& fn) -> std::shared_ptr<entity<R>>;
template <typename R>
struct entity
{
template <invocable_r<R> Cb>
entity(Cb&& fn)
: fn_{ std::move(fn) }
{}
// *************** Q ***************
// How to match this declaration to the outside declaration?
template<invocable_r<R> Cb>
friend auto create_entity<R>(Cb&& fn) -> std::shared_ptr<entity<R>>;
// *************** /Q ***************
std::packaged_task<R()> fn_;
};
/* impl.cpp */
template <typename R, invocable_r<R> Cb>
auto create_entity(Cb&& fn) -> std::shared_ptr<entity<R>> {
return std::make_shared<entity<R>>(std::forward<Cb>(fn));
}
int main()
{
create_entity<int>([]{ printf("Hello"); return 10; });
}

错误:

<source>:28:17: error: invalid use of template-id 'create_entity<R>' in declaration of primary template
28 |     friend auto create_entity<R>(Cb&& fn) -> std::shared_ptr<entity<R>>;
|                 

这里的问题是在friend声明中不允许部分特化(参见https://en.cppreference.com/w/cpp/language/friend#Template_friends)(或一般的函数模板)。

我可以使用以下步骤使其工作:

  1. 我们用函数调用操作符将代码从create_entity移动到struct:
template <typename R, invocable_r<R> Cb>
struct create_entity_s {
template<typename U = Cb>
create_entity_s(U&& fn) : m_fn{ std::forward<U>(fn) } {}
auto operator()() {
auto ret = std::make_shared<entity<R>>(std::move(m_fn));
// Test if we're really a friend by accessing a private member
ret->_i = 5;
return ret;
}
Cb m_fn;
};
  1. 现在我们定义create_function如下,返回上述struct的实例:
template <typename R, invocable_r<R> Cb>
auto create_entity(Cb&& fn) {
return create_entity_s<R, Cb>{ std::forward<Cb>(fn) };
}
  1. 更改friend声明。在这里,通过使用U,我们确保没有部分专门化。然而,这有一个缺点,我们现在与create_entity_s的每个实例化都是朋友:
template<typename U, invocable_r<U> Cb>
friend struct create_entity_s;
  1. 使用create_entity(注意末尾的括号,以便调用函数对象):
create_entity<int>([]{ printf("Hello"); return 10; })();

下面是编译器资源管理器上的完整工作示例:https://godbolt.org/z/MPKe5Me6v

我还没有能够使它只使用函数模板工作,但struct的技巧肯定是有效的。我也没有花太多的时间在这上面,所以请原谅任何不完美之处,但总的原则是有效的!

希望这对你有帮助!

最新更新