我想知道是否有一种使用C++17标准编写类似于以下代码的实用方法:
#include <string>
#include <functional>
#include <unordered_map>
template <class Arg>
struct Foo
{
using arg_type = Arg;
using fun_type = std::function< void(Arg&) >;
fun_type fun;
void call( Arg& arg ) { fun(arg); }
};
struct Bar
{
using map_type = std::unordered_map<std::string,Foo>; // that's incorrect
map_type map;
auto& operator[] ( std::string name ) { return map[name]; }
};在上面的代码中,类Foo的模板参数对应于一元函数的输入类型,它不返回任何内容。不同模板类型的Foo的不同实例对应于接受不同类型参数的函数。类Bar只是为了给这些函数分配一个名称,但是很明显,当前的映射声明是不正确的,因为它需要知道Foo的模板类型。
或者是真的?
发布于 2021-11-03 22:38:43
不幸的是,用编译时检查来完成这个任务是不可行的。但是,您可以为该功能提供运行时检查。
映射的值类型只能是一种类型,而Foo<T>是每个T的不同类型。但是,我们可以通过为每个Foo<T>提供一个公共基类、有一个指向它的指针映射以及使用一个虚拟函数将call()分派给适当的子类来解决这个问题。
然而,对于这一点,参数的类型也必须总是相同的。正如@MSalters所提到的,std::any可以在这方面提供帮助。
最后,我们可以使用pimpl模式包装所有这些,这样看起来只有一个整洁的Foo类型:
#include <cassert>
#include <string>
#include <functional>
#include <any>
#include <unordered_map>
#include <memory>
struct Foo {
public:
template<typename T, typename FunT>
void set(FunT fun) {
pimpl_ = std::make_unique<FooImpl<T, FunT>>(std::move(fun));
}
// Using operator()() instead of call() makes this a functor, which
// is a little more flexible.
void operator()(const std::any& arg) {
assert(pimpl_);
pimpl_->call(arg);
}
private:
struct IFooImpl {
virtual ~IFooImpl() = default;
virtual void call( const std::any& arg ) const = 0;
};
template <class Arg, typename FunT>
struct FooImpl : IFooImpl
{
FooImpl(FunT fun) : fun_(std::move(fun)) {}
void call( const std::any& arg ) const override {
fun_(std::any_cast<Arg>(arg));
}
private:
FunT fun_;
};
std::unique_ptr<IFooImpl> pimpl_;
};
// Usage sample
#include <iostream>
void bar(int v) {
std::cout << "bar called with: " << v << "\n";
}
int main() {
std::unordered_map<std::string, Foo> table;
table["aaa"].set<int>(bar);
// Even works with templates/generic lambdas!
table["bbb"].set<float>([](auto x) {
std::cout << "bbb called with " << x << "\n";
});
table["aaa"](14);
table["bbb"](12.0f);
}https://stackoverflow.com/questions/69832385
复制相似问题