【问题标题】:Constructor template argument deduction with std::function as parameter以 std::function 作为参数的构造函数模板参数推导
【发布时间】:2017-08-11 06:34:50
【问题描述】:

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

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

}

这个类的构造函数接受一个函数参数,其参数类型为T,该参数派生自某个基本类型BaseT,并将此函数存储在使用std::type_infoT 作为键的向量映射中。

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

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

省略显式的&lt;MyT&gt; 也不起作用,因为模板参数无法从 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) { ... });

【问题讨论】:

  • @StoryTeller:很好,我更新了问题!

标签: c++ templates


【解决方案1】:

使用普通模板参数代替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 specialisations

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

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&amp;,因此您可能希望将函数限制为只能使用可以编译的内容进行调用:

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) {
    // ...
}

【讨论】:

  • 当我想将传递的函数存储在向量中时,这会起作用吗?对不起,原来的问题有一些错误,因为存储函数的部分搞砸了。我更新了这个。
  • 您可以存储std::function的向量,但在构造函数中接收const T&amp;
  • @csk 我更新了示例以更准确地反映您最初所做的工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-29
  • 1970-01-01
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
  • 2018-11-19
  • 2011-10-21
相关资源
最近更新 更多