构造函数模板参数推导,其中 std::function 作为参数



我有一个带有如下所示的模板构造函数的类:

class Foo {
private:
std::unordered_map<std::type_index, std::vector<std::function<void(BaseT*)>>> funcs;
public:
template<class T> Foo(const std::function<void(T* arg)>& func) {
auto funcToStore = [func](BaseT* a) { func(static_cast<T*>(a)); };
this->funcs[std::type_index(typeid(T))].push_back(funcToStore);
}
}

此类的构造函数采用一个函数参数,其中包含从某个基类型派生TBaseT类型的参数,并将此函数存储在利用键Tstd::type_info的向量映射中。

由于这是一个模板构造函数而不是普通函数,因此显式指定模板参数将不起作用,因为这是不允许的语法:

Foo* foo = new Foo<MyT>([](MyT* arg) { ... });

省略显式<MyT>也不起作用,因为无法从 lambda 参数类型推断出模板参数。

因此,一种解决方案是将lambda包装在std::function对象中:

Foo* foo = new Foo(std::function<void(MyT*)>([](MyT* arg) { ... }));

但这显然不是一个很好的可读语法。

到目前为止,我想出的最好的方法是使用别名std::function

template<class T> using Func = std::function<void(T*)>;
Foo* foo = new Foo(Func<MyT>([](MyT* arg) { ... }));

这更短,并且在 lambda 参数中使用auto关键字时,我只需要指定一次实际类型MyT,因此这似乎是一个很好的解决方案。

但是,还有其他甚至更短的解决方案吗?这样就没有必要包裹lambda了?喜欢:

Foo* foo = new Foo([](MyT* arg) { ... });

使用普通模板参数而不是std::function

class Foo {
std::unordered_map<size_t, std::vector<BaseT*>> funcs;
public:
template<class T> Foo(const T& func) {
// ...
}
};

现在,扣除将正确发生,并且您的代码不会受到std::function开销的影响。

如果要获取 lambda 的第一个参数的类型怎么办?

你必须做这样的东西:

template<typename T>
struct function_traits : function_traits<&T::operator()> {};
template<typename R, typename C, typename... Args>
struct function_traits<R(C::*)(Args...) const> {
using arguments = std::tuple<Args...>;
using result = R;
};

当然,如果你想支持所有可能的功能类型,你需要32个专业。

现在,如果需要,您可以提取参数类型甚至返回类型:

template<class T> Foo(const T& func) {
using Arg = std::tuple_element_t<0, typename  function_traits<T>::arguments>;
auto funcToStore = [func](BaseT* a) { func(static_cast<Arg>(a)); };
funcs[typeid(Arg).hash_code()].push_back(funcToStore);
}

此外,由于您在构造函数中收到const T&,因此您可能希望将函数约束为只能使用可编译的内容进行调用:

template<typename T>
using is_valid_foo_function = std::is_convertible<
BaseT*, // form
std::tuple_element_t<0, typename function_traits<T>::arguments> // to
>;

并像这样使用约束:

template<class T, std::enable_if_t<is_valid_foo_function<T>::value>* = nullptr>
Foo(const T& func) {
// ...
}

相关内容

最新更新