作为我最后一个问题的延续,我现在的情况是,我想声明一个函数作为依赖于类模板参数(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)(或一般的函数模板)。
我可以使用以下步骤使其工作:
- 我们用函数调用操作符将代码从
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;
};
- 现在我们定义
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) };
}
- 更改
friend
声明。在这里,通过使用U
,我们确保没有部分专门化。然而,这有一个缺点,我们现在与create_entity_s
的每个实例化都是朋友:
template<typename U, invocable_r<U> Cb>
friend struct create_entity_s;
- 使用
create_entity
(注意末尾的括号,以便调用函数对象):
create_entity<int>([]{ printf("Hello"); return 10; })();
下面是编译器资源管理器上的完整工作示例:https://godbolt.org/z/MPKe5Me6v
我还没有能够使它只使用函数模板工作,但struct
的技巧肯定是有效的。我也没有花太多的时间在这上面,所以请原谅任何不完美之处,但总的原则是有效的!
希望这对你有帮助!