如何返回对空的'对象

时间:2016-10-16 12:19:33

标签: c++ reference return

我今天试图解决的问题是你想要返回引用的问题,但实际上你不能这样做,因为在某些特定情况下你会返回一些空的'。只是为了澄清,像这样:

std::array<std::string, 5> cStrings /* do something to init */

const std::string& get_my_string(/* args */) {
    if(i_know_what_to_return()) {
        /* 
         * returning happily something in 
         * cStrings if I know what
         */
    } else {
        /* oeps... i need to return something, I don't have :-( */

        return std::string(); // DON'T TRY THIS AT HOME!
    }
}

我想知道是否没有通用的方法来避免在整个地方创建空对象,或者更糟糕的是,开始返回对象的副本。所以我想知道我是否无法创建某种模板类,允许我告诉编译器为我管理这个。这是我的方法(尽管它只是众多方法中的一个,所有模板声明中的变化很小,因为这会产生错误)

template<typename T, decltype(T) V>
struct empty_reference {
    using type = T;
    static const T static_object_;
    operator T&() const {
        return static_object_;
    }
};

template<typename T, decltype(T) V>
const typename empty_reference<T, V>::type
    empty_reference<T, V>::static_object_ = V;

不幸的是,这不起作用(我得到了错误&#39; decltype(T)V&#39;说'decltype期望表达式不是类型&#39;),但我想这主要是因为我在模板声明中遗漏了一些东西。 最后我希望通过返回

来使用这个类
return empty_reference<std::string, std::string()>();

所以我在这里有三个问题;

  1. 这可能是可能的
  2. 我该怎么做才能做到这一点,我该怎样转向&#39; decltype(T)V&#39;在编译期间仍在评估时,告诉编译器V应该是T类型吗?
  3. 这是一个很好的方法,还是有一个更简单/更好的解决方案来解决这个问题?

3 个答案:

答案 0 :(得分:1)

不完全相同,但您可以将字符串作为函数参数而不是返回值,并依赖于临时值绑定到const引用的事实。
举个例子:

#include <string>
#include <iostream>
#include <utility>

struct S {
    template<typename F>
    void get(bool b, F &&f) {
        std::forward<F>(f)(b ? s : "");
    }

    std::string s{"foo"};
};

int main() {
    S s;
    s.get(true, [](const auto &str) { std::cout << str << std::endl; });
    s.get(false, [](const auto &str) { std::cout << str << std::endl; });
}

如果像Boost这样的库不是您项目的一部分并且您不想包含它们,那么这可能是一个有效的替代方案。

否则,正如其他人所提到的那样,您可以选择即将推出的名为std::optional的实用程序,并将其与std::reference_wrapper结合使用,如下所示:

#include <string>
#include <iostream>
#include <experimental/optional>
#include <functional>

struct S {
    std::experimental::optional<std::reference_wrapper<std::string>> get(bool b) {
        return b ? std::ref(s) : std::experimental::optional<std::reference_wrapper<std::string>>{};
    }

    std::string s{"foo"};
};

int main() {
    S s;

    auto opt1 = s.get(true);
    std::cout << (opt1 ? opt1->get() : "-") << std::endl;

    auto opt2 = s.get(false);
    std::cout << (opt2 ? opt2->get() : "-") << std::endl;
}

确实非常丑陋。请注意,std::optional应通过其operator bool或成员方法has_value进行验证,以确保其中包含值。

不幸的是,您不能直接使用std::reference_wrapper作为返回值,因为它不能(让我说)_empty)。换句话说,如果要构造此类对象,则必须将有效引用传递给其构造函数。

另一种方法是使用类似下面的模板类:

#include <string>
#include <type_traits>
#include <iostream>

template<typename T>
struct defval {
    static const std::decay_t<T> value;
};

template<typename T>
const std::decay_t<T> defval<T>::value = T{};

struct S {  
    const std::string & get(bool b) {
        return b ? str : defval<std::string>::value;
    }

    std::string str{"foo"};
};

int main() {
    S s;
    std::cout << s.get(true) << std::endl;
    std::cout << s.get(false) << std::endl;
}

请注意,您必须专门针对那些不是默认构造的类型。

答案 1 :(得分:0)

引用必须绑定到对象。因此,您无法在案例中返回引用。您的选择是:

  1. 返回(非拥有)指针。
  2. 返回boost::optional之类的内容,它可以包含引用(与即将添加的std::optional不同)。

答案 2 :(得分:-1)

所以在某些时候我想出来了,至少我猜。

template<typename T>
struct empty_reference {
    using type = T;
    static const type static_object_;
    operator const type&() {
        return static_object_;
    }
};

template<typename T>
const typename empty_reference<T>::type
    empty_reference<T>::static_object_ = {};

概念是编译器将为您使用它的所有类型“生成”一个静态“static_object_”成员。因此,对于您使用empty_reference的任何类型,在应用程序中生成静态对象并在请求它们时返回(即,如果在两个不同的文件中返回empty_reference<int>,则将返回对相同int值的引用)。

关键是要完全删除'decltype(T) V并使用通用初始化(即{})以确保调用任何'空'构造函数来初始化对象。

缺点是引用需要保持不变(否则你可以编辑'空引用')并且类型需要使用空构造函数构造。

所以我想剩下的唯一问题是它是否是一个好主意(我不会只是我自己的想法)。很高兴收到任何其他建议/反馈: - )