【问题标题】:C++ - Function with multiple parameter packs and a std::function as argumentC++ - 具有多个参数包和 std::function 作为参数的函数
【发布时间】:2021-09-04 21:44:37
【问题描述】:

我正在尝试在 C++ 中创建一个自动解决依赖关系的 IoC 容器。

为此,我创建了一个带有两个可变参数包的函数,声明如下:

template <class T, typename ... TDependencies, typename... TArgs> 
void Register(std::function<std::shared_ptr<T> (std::shared_ptr<TDependencies> ...,
TArgs ...)> && pFactory)

显然,编译器在提供时似乎无法匹配

Register<Foo, Bar>(std::function<std::shared_ptr<Foo>(std::shared_ptr<Bar>)>(
[](std::shared_ptr<Bar> bar){return std::make_shared<Foo>(bar);}));

编译错误说

note: candidate: 'void Container::Register(std::function<std::shared_ptr<_Tp>
(std::shared_ptr<TDependencies>..., TArgs ...)>&&)
[with T = Foo; TDependencies = {Bar}; TArgs = {std::shared_ptr<Bar>}]'

显然,它匹配std::shared_ptr&lt;Bar&gt; 两次。如何让编译器不匹配 TArgs 中的shared_ptr

【问题讨论】:

  • std::shared_ptr&lt;T (std::shared 应该是std::shared_ptr&lt;T&gt;(std::shared ?
  • 是的,我复制的时候搞砸了。感谢您指出这一点
  • 您能否将您的代码格式化为最多 80 个字符。您的大部分代码都是隐藏的,必须在两个实例上水平滚动非常繁琐且难以阅读。
  • 如果任何参数本身是共享指针,则函数签名不明确。
  • @Quimby 但为什么呢?什么规则阻止此模板明确地将 TArgs... 匹配为空包?

标签: c++ c++17 variadic-templates


【解决方案1】:

与其尝试直接从pFactory 参数类型推断TDependencies,不如编写一个类型特征来从整个参数包中获取依赖关系。与boost::mp11

template <class>
struct is_shared_ptr : std::false_type {};

template <class T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};

namespace mp11 = ::boost::mp11;

template <class... Ts>
using register_traits = mp11::mp_partition<mp11::mp_list<Ts...>, is_shared_ptr>;

template <class T, class F, class... TDependencies, class... TArgs>
void RegisterImpl(F && pFactory,
                  mp11::mp_list<
                      mp11::mp_list<std::shared_ptr<TDependencies>...>,
                      mp11::mp_list<TArgs...>>);

template <class T, class... Ts> 
void Register(std::function<std::shared_ptr<T> (Ts...)> && pFactory)
{
    return RegisterImpl<T>(
        std::forward<std::function<std::shared_ptr<T> (Ts...)>>(pFactory),
        register_traits<Ts...>{});
}

并称它为:

Register(std::function{[] (std::shared_ptr<Bar> bar) {
    return std::make_shared<Foo>(bar);
}});

Try it on godbolt.org.

如果boost::mp11 不是一个选项,您可以通过以下方式实现自己的partition 模板元函数:

template <class...>
struct list {};

namespace detail {
template <class L, template <class...> class P, class T, class F, class = void>
struct partition;

template <class Next, class... Ls,
          template <class...> class P, class T, class... Fs>
struct partition<list<Next, Ls...>, P, T, list<Fs...>,
                 std::enable_if_t<!P<Next>::value>> :
       partition<list<Ls...>, P, T, list<Fs..., Next>> {};

template <class Next, class... Ls,
          template <class...> class P, class... Ts, class F>
struct partition<list<Next, Ls...>, P, list<Ts...>, F,
                 std::enable_if_t<P<Next>::value>> :
       partition<list<Ls...>, P, list<Ts..., Next>, F> {};

template <template <class...> class P, class T, class F>
struct partition<list<>, P, T, F> { using type = list<T, F>; };
} // namespace detail

template <class L, template <class...> class P>
using partition = typename detail::partition<L, P, list<>, list<>>::type;

template <class... Ts>
using register_traits = partition<list<Ts...>, is_shared_ptr>;

template <class T, class F, class... TDependencies, class... TArgs>
void RegisterImpl(F && pFactory,
                  list<list<std::shared_ptr<TDependencies>...>, list<TArgs...>>);

Try it on godbolt.org.

其余代码将保持不变。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-25
    • 2021-07-12
    相关资源
    最近更新 更多