- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我正在尝试编写一个带有接受 std::function 对象作为参数的重载构造函数的类,但是当然 每件该死的事情都可以隐式转换为任何签名的 std::function .这自然很有帮助。
例子:
class Foo {
Foo( std::function<void()> fn ) {
...code...
}
Foo( std::function<int()> fn ) {
...code...
}
};
Foo a( []() -> void { return; } ); // Calls first constructor
Foo b( []() -> int { return 1; } ); // Calls second constructor
这不会编译,提示两个构造函数本质上相同且模棱两可。这当然是胡说八道。我已经尝试过 enable_if、is_same 和其他一些东西。接受函数指针是不可能的,因为这会阻止有状态 lambda 的传递。肯定有办法做到这一点吗?
恐怕我的模板技巧有点欠缺。普通的模板类和函数很容易,但是用编译器玩愚蠢的 bug 有点不合我意。有人可以帮帮我吗?
我知道以前有人问过这个问题的变体,但它们通常侧重于普通函数而不是构造函数;或通过参数重载而不是返回类型。
最佳答案
这里有一些常见的情况,以及为什么我不认为 std::function
适合他们:
struct event_queue {
using event = std::function<void()>;
std::vector<event> events;
void add(event e)
{ events.emplace_back(std::move(e)); }
};
在这种简单的情况下,特定签名的仿函数被存储。从这个角度来看,我的建议似乎很糟糕,不是吗?会出什么问题?像 queue.add([foo, &bar] { foo.(bar, baz); })
这样的事情运行良好,类型删除正是您想要的功能,因为可能会存储异构类型的仿函数,所以它的成本不是问题。这实际上是可以使用 std::function<void()>
的一种情况。在add
的签名中是可以接受的。但请继续阅读!
在未来的某个时刻,您意识到某些事件在被回调时可能会使用某些信息——因此您尝试:
struct event_queue {
using event = std::function<void()>;
// context_type is the useful information we can optionally
// provide to events
using rich_event = std::function<void(context_type)>;
std::vector<event> events;
std::vector<rich_event> rich_events;
void add(event e) { events.emplace_back(std::move(e)); }
void add(rich_event e) { rich_events.emplace_back(std::move(e)); }
};
问题在于像 queue.add([] {})
这样简单的东西。仅保证适用于 C++14——在 C++11 中,允许编译器拒绝代码。 (最近足够多的 libstdc++ 和 libc++ 是在这方面已经遵循 C++14 的两个实现。)像 event_queue::event e = [] {}; queue.add(e);
这样的东西虽然仍然有效!因此,只要您针对 C++14 进行编码,就可以使用它。
然而,即使使用 C++14,std::function<Sig>
的这个特性也是如此。可能并不总是做你想做的事。考虑以下内容,它现在是无效的,并且在 C++14 中也是如此:
void f(std::function<int()>);
void f(std::function<void()>);
// Boom
f([] { return 4; });
也有充分的理由:std::function<void()> f = [] { return 4; };
不是错误并且工作正常。返回值被忽略和遗忘。
有时 std::function
与模板推导一起使用,如 this question 所示。和 that one .这往往会增加更多的痛苦和困难。
简单地说,std::function<Sig>
在标准库中没有特别处理。它仍然是一个用户定义的类型(在某种意义上,它不像 int
),它遵循正常的重载决议、转换和模板推导规则。这些规则非常复杂并且相互交互——它不是为界面用户提供的服务,他们必须牢记这些规则才能将可调用对象传递给它。 std::function<Sig>
具有悲剧性的吸引力,它看起来有助于使界面简洁且更具可读性,但只要您不重载这样的界面,这确实是正确的。
我个人有很多可以根据签名检查类型是否可调用的特征。结合 expressive EnableIf
or Requires
clauses我仍然可以保持可接受的可读界面。反过来,结合一些 ranked overloads我大概可以实现“如果仿函数产生可转换为 int
的东西,则调用此重载”的逻辑。当不带参数调用时,否则回退到此重载'。这可能看起来像:
class Foo {
public:
// assuming is_callable<F, int()> is a subset of
// is_callable<F, void()>
template<typename Functor,
Requires<is_callable<Functor, void()>>...>
Foo(Functor f)
: Foo(std::move(f), select_overload {})
{}
private:
// assuming choice<0> is preferred over choice<1> by
// overload resolution
template<typename Functor,
EnableIf<is_callable<Functor, int()>>...>
Foo(Functor f, choice<0>);
template<typename Functor,
EnableIf<is_callable<Functor, void()>>...>
Foo(Functor f, choice<1>);
};
请注意,is_callable
精神中的特征检查给定的签名——也就是说,它们检查一些给定的参数和一些预期的返回类型。他们不进行内省(introspection),因此他们在面对例如重载的仿函数。
关于c++ - 如何在 std::function 的签名上重载构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16492470/
我正在开发一个小型图书馆,我需要做的一件事是让访问者访问一些数据并返回结果。 在一些较旧的 C++ 代码中,访问者需要声明一个 typedef return_type .例如,boost::stati
我正在尝试使用std:map类型的键和值制作std::any Visual Studio 2017 std::map m("lastname", "Ivanov"); std::cout (m["la
我已经在 C++ 的 map 中声明了一个集合为 std::map> .如何循环访问或打印设定值? 最佳答案 如果你知道如何迭代 std::map或 std::set单独地,您应该可以毫无问题地组合迭
如何循环? 我已经试过了: //----- code std::vector >::iterator it; for ( it = users.begin(); it != users.end();
我有两个用例。 A.我想同步访问两个线程的队列。 B.我想同步两个线程对队列的访问并使用条件变量,因为其中一个线程将等待另一个线程将内容存储到队列中。 对于用例 A,我看到了使用 std::lock_
我正在查看这两种类型特征的文档,但不确定有什么区别。我不是语言律师,但据我所知,它们都适用于“memcpy-able”类型。 它们可以互换使用吗? 最佳答案 不,这些术语不能互换使用。这两个术语都表示
我有以下测试代码,其中有一个参数 fS,它是 ofstream 的容器: #include #include #include #include int
这是这个问题的延续 c++ function ptr in unorderer_map, compile time error 我试图使用 std::function 而不是函数指针,并且只有当函数是
std::unordered_map str_bool_map = { {"a", true}, {"b", false}, {"c", true} }; 我们可以在此映射上使
我有以下对象 std::vector> vectorList; 然后我添加到这个使用 std::vector vec_tmp; vec_tmp.push_back(strDRG); vec_tmp.p
为什么 std::initializer_list不支持std::get<> , std::tuple_size和 std::tuple_element ?在constexpr中用得很多现在的表达式,
我有一个像这样定义的变量 auto drum = std::make_tuple ( std::make_tuple ( 0.3f , Ex
假设我有一个私有(private)std::map在我的类(class)里std::map 。我怎样才能将其转换为std::map返回给用户?我想要下面的原型(prototype) const std
假设我有一个私有(private)std::map在我的类(class)里std::map 。我怎样才能将其转换为std::map返回给用户?我想要下面的原型(prototype) const std
问题 我正在尝试将 lambda 闭包传递给 std::thread,它使用任意封闭参数调用任意封闭函数。 template std::thread timed_thread(Function&& f
我想创建一个模板类,可以容纳容器和容器的任意组合。例如,std::vector或 std::map ,例如。 我尝试了很多组合,但我必须承认模板的复杂性让我不知所措。我编译的关闭是这样的: templ
我有一个 std::vector>我将其分配给相同类型的第二个 vector 。 我收到这个编译器错误: /opt/gcc-8.2.0/include/c++/8.2.0/bits/stl_algob
有时候,我们有一个工厂可以生成一个 std::unique_ptr vector ,后来我们想在类/线程/你命名的之间共享这些指针。因此,最好改用 std::shared_ptr 。当然有一种方法可以
这个问题在这里已经有了答案: Sorting a vector of custom objects (14 个答案) 关闭 6 年前。 我创建了一个 vector vector ,我想根据我定义的参
我有三个类(class)成员: public: std::vector > getObjects(); std::vector > getObjects() const; privat
我是一名优秀的程序员,十分优秀!