【问题标题】:Return specific map entry instead of map::end if no map entry was found如果没有找到地图条目,则返回特定的地图条目而不是 map::end
【发布时间】:2016-07-12 14:13:50
【问题描述】:

在 switch 语句中,如果其他情况都不适用,您可以指定默认返回值。 我想用 std::map 做类似的事情,但我不确定是否可能: 如果没有适合搜索的键,我可以让地图返回特定的值/条目而不是 map::end 吗?

示例(这是我最终想做的 - 我知道代码不起作用):

std::map<std::string, void* (*) (DataObject*)> commands; //Mapping functions to keys
commands["Test"] = function(DataObject d){doSomething();}
commands[Nothing else applies] = function(DataObject d){doSomethingElse();}

commands.find("Test")(someDataObject); // doSomething(); happens
commands.find("blabla")(someDataObject); //doSomethingElse(); happens because no other entry was found

这样的事情可能吗?

(另外,我在这个例子中使用函数指针的方式可能根本不起作用 - 我可以使用 cpp11 的 lambda 表达式来完成我想做的事情吗?)

我不想使用 switch 子句,也不想使用 if(m.find(x) != m.end()) 子句,也不想使用更多 if-else。

【问题讨论】:

    标签: c++ dictionary function-pointers


    【解决方案1】:

    您可以使用自己的具有默认值的函子:

    struct MyFunctor: public std::function<void(DataObject*)> {
    
        template <typename F>
        MyFunctor (F &&f) : std::function<void(DataObject*)> (std::forward<F> (f)) { }
    
        MyFunctor () : std::function<void(DataObject*)> (doSomethingElse) { }
    
    };
    

    假设doSomethingElse 是一个已经定义好的函数。

    那么你可以简单地做:

    std::map<std::string, MyFunctor> commands; //Mapping functions to keys
    commands["Test"] = [] (DataObject *) { }; // Assign from lambda
    
    DataObject *myDataObject= new DataObject();
    commands["Not Defined"] (myDataObject); // Call doSomethingElse
    

    您可以使用MyFunctor 的模板轻松获得不同的默认值(在编译时定义):

    template <void (*DefaultFn) (DataObject*)>
    struct MyFunctor: public std::function<void(DataObject*)> {
    
        template <typename F>
        MyFunctor (F &&f) : std::function<void(DataObject*)> (std::forward<F> (f)) { }
    
        MyFunctor () : std::function<void(DataObject*)> (DefaultFn) { }
    
    };
    
    std::map<std::string, MyFunctor<doSomethingElse>> commands;
    

    【讨论】:

    • 哇,这看起来棒极了,谢谢!我现在只需要真正理解它,哈哈。 (来自 Java 背景,很多 C++ 的东西对我来说还是新的)
    • 您介意解释一下“&&”在这种情况下的确切含义吗?我读到它代表 RValue,但在这种情况下我还没有使用它。此外,“覆盖”究竟是如何工作的(根据定义,映射中每个键的值应该是 MyFunctor 类型,但代码允许简单地用 lambda 覆盖它 - 这很好,但它是如何工作的? ) - 等等,nvm,这是因为 MyFunctor 继承自函数,对吧?那么当我们传递一个 lambda 时,MyFunctor 会使用该 lambda 构造,然后调用 forward 方法?上一篇:为什么用struct而不是class?
    • @user2065501 &amp;&amp; 在这里是universal reference,这意味着它可以接受左值或右值并进行完美转发(在这段代码中你不应该真正担心它)。代码原样允许您使用可以传递给std::function 的构造函数的任何东西来构造MyFunctor,它是一个lambda(当您分配一个lambda 时,您使用给定的lambda 创建一个新的MyFunctor ,它只是将它传递给它的父 std::function)。 struct 而不是 class 只是习惯,如果需要,请使用 class
    • 哦,还有:使用您帖子末尾的模板版本,是否有可能在地图声明的同一行中“创建”doSomethingElse?基本上,std::map&lt;std::string, MyFunctor&lt;[](DataObject*){}&gt;&gt;?我试过了,lambda是不允许的,但是还有其他可能吗?
    • 不,在这种情况下不可能,因为您提供的参数必须在编译时可用(lambda 不是)。
    【解决方案2】:

    不,您不能使用 std::map 获得该功能,因为当找不到密钥并且您无法取消引用它时,std::map::find() 返回等于 end() 的迭代器。因此,您要么需要使用 if( m.find( "blah" ) == m.end() ) 子句,要么使用 std::map 的其他内容,或者编写您自己的具有您需要的功能的类。

    您可能有特殊的值类型,默认状态将具有您需要的功能,然后通过std::map::operator[] 而不是std::map::find() 访问它,但这会产生副作用 - 对于您搜索的每个键,新条目将添加到地图中.我怀疑这会比拥有一个 if() 子句更好,但这取决于你。

    【讨论】: