我今天试图解决的问题是你想要返回引用的问题,但实际上你不能这样做,因为在某些特定情况下你会返回一些空的'。只是为了澄清,像这样:
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()>();
所以我在这里有三个问题;
答案 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)
引用必须绑定到对象。因此,您无法在案例中返回引用。您的选择是:
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
并使用通用初始化(即{}
)以确保调用任何'空'构造函数来初始化对象。
缺点是引用需要保持不变(否则你可以编辑'空引用')并且类型需要使用空构造函数构造。
所以我想剩下的唯一问题是它是否是一个好主意(我不会只是我自己的想法)。很高兴收到任何其他建议/反馈: - )