繁体   English   中英

以std :: function为参数的构造函数模板参数推导

[英]Constructor template argument deduction with std::function as parameter

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

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);
    }

}

此类的构造函数使用带有从基本类型BaseT派生的T类型参数的函数参数,并将该函数存储在使用T std::type_info作为键的向量映射中。

由于这是模板构造函数,而不是普通函数,因此明确指定template参数无效,因为这是不允许的语法:

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

<MyT>式的<MyT>也是行不通的,因为无法从lambda参数类型推导出template参数。

因此,一种解决方案是将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&收到了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) {
    // ...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM