What should the return type be when the function might not have a value to return?

后端 未结 6 967
被撕碎了的回忆
被撕碎了的回忆 2021-01-01 17:25

In the old days, you might have a function like this:

const char* find_response(const char* const id) const;

If the item could not be found

相关标签:
6条回答
  • 2021-01-01 17:43

    boost::optional. It was specifically designed for this kind of situation.

    Note, it will be included in upcoming C++14 standard as std::optional. Update: After reviewing national body comments to N3690, std::optional was voted out from C++14 working paper into a separate Technical Specification. It is not a part of the draft C++14 as of n3797.

    Compared to std::unique_ptr, it avoids dynamic memory allocation, and expresses more clearly its purpose. std::unique_ptr is better for polymorphism (e.g. factory methods) and storing values in containers, however.

    Usage example:

    #include <string>
    #include <boost/none.hpp>
    #include <boost/optional.hpp>
    
    class A
    {
    private:
        std::string value;
    public:
        A(std::string s) : value(s) {}
    
        boost::optional<std::string> find_response(const std::string& id) const
        {
            if(id == value)
                return std::string("Found it!");
            else
                return boost::none;
            //or
            //return boost::make_optional(id == value, std::string("Found it!"));
        }
    
        //You can use boost::optional with references,
        //but I'm unfamiliar with possible applications of this.
        boost::optional<const std::string&> get_id() const
        {
            return value;
        }
    };
    
    #include <iostream>
    
    int main()
    {
        A a("42");
        boost::optional<std::string> response = a.find_response("42"); //auto is handy
        if(response)
        {
            std::cout << *response;
        }
    }
    
    0 讨论(0)
  • 2021-01-01 17:55

    What would be the most elegant modern C++ way?

    There's, as always, not just one solution to this problem.

    If you decide to go for any solution that references the original resonse instance, you're on a slippery road when it comes to aliasing and memory management, especially in a multi threaded environment. By copying the response to the caller, no such issues arises.

    Today, I would do this:

    std::unique_ptr<std::string> find_response(const std::string& id) const;
    

    That way, you can check for nullptr as "in the olden days" and it's 100% clear who's responsibility it is to clear up the returned instance: the caller.

    The only downside I see of this, is the additional copy of the response string, but don't dismiss that as a downside until measured and proven so.

    Another way is to do as is done when searching std::set<> and std::map<> - return a std::pair<bool, const char*> where one value is bool is_found and the other is const char* response. That way you don't get the "overhead" of the additional response copy, only of the returned std::pair<> which is likely to be maximally optimized by the compiler.

    0 讨论(0)
  • 2021-01-01 17:57

    I think the second way is better. Or you can write like this:

    int find_response(const std::string& id, std::string& value) const;
    

    if this function return -1, it tells that you don't find the response.

    0 讨论(0)
  • 2021-01-01 18:03

    Use of pointers in C++ is forgiven if you need to return a nullable entity. This is widely accepted. But of course bool find_response(const std::string& id, std::string& value) const; is quite verbose. So it is a matter of your choice.

    0 讨论(0)
  • 2021-01-01 18:09

    If the function is returning a string by reference, but needs the ability to indicate that no such string exists, the most obvious solution is to return a pointer, which is basically a reference that can be null, i.e. exactly what was sought after.

    const std::string* find_response(const std::string& id) const;
    
    0 讨论(0)
  • 2021-01-01 18:09

    There are several good solutions here already. But for the sake of completeness I'd like to add this one. If you don't want to rely on boost::optional you may easily implement your own class like

    class SearchResult
    {
        SearchResult(std::string stringFound, bool isValid = true)
            : m_stringFound(stringFound),
            m_isResultValid(isValid)
        { }
    
        const std::string &getString() const { return m_stringFound; }
        bool isValid() const { return m_isResultValid; }
    
    private:
        std::string m_stringFound;
        bool m_isResultValid;
    };
    

    Obviously your method signature looks like this then

    const SearchResult& find_response(const std::string& id) const;
    

    But basically that's the same as the boost solution.

    0 讨论(0)
提交回复
热议问题