有没有办法 return 一个新对象或从函数引用现有对象?
Is there a way to return either a new object or reference to existing object from a function?
我正在尝试编写一个函数,它可以 return 对作为第一个参数传递的现有对象的引用(如果它处于正确状态)或创建并 return 一个新的使用文字作为第二个参数传递的对象(默认)。
如果一个函数不仅可以接受字面量,还可以接受另一个现有对象作为第二个(默认)参数和 return 对它的引用,那就更好了。
下面是一个简单的实现,但它做了很多不需要的工作:
如果使用左值作为第二个(默认)参数调用,它会调用为 return 选择的参数的复制构造函数。理想情况下,对对象的引用应该 returned.
如果使用文字作为第二个(默认)参数调用,它会调用构造函数、复制构造函数和析构函数,即使没有为 return 选择第二个(默认)参数。如果在不调用复制构造函数或析构函数的情况下构造对象并将其 returned 作为右值引用会更好。
std::string get_or_default(const std::string& st, const std::string& default_st) {
if (st.empty()) return default_st
else return st;
}
有没有一种方法可以更有效地完成此操作,同时仍然对调用者保持简单?如果我是正确的,这需要一个函数来根据函数内部做出的 运行 时间决定来更改 return 类型,但我想不出一个简单的调用者解决方案。
好吧,这里有一些东西。
要直接表达您的要求,您可以使用 std::variant<std::string, std::string&>
之类的东西作为您的函数 return 类型。虽然我没有检查变体是否可以存储引用。
或者来自第三方库的一些等价物。要么<>?
您还可以编写自己的 class 包装字符串和字符串引用。
(不是真实代码)
struct StringOrRef {
enum class Type {Value, Ref} type;
union {
std::string value;
std::reference_wrapper<const std::string> ref;
};
...
};
检查主题:C++ 中的判别联合。
但我认为你的例子有更大的问题!
请考虑数据的所有权。 std::string 获取传递的数据的所有权。这就是它复制数据的原因。因此,当你的函数 returns - 被调用者确定它有一个数据并且只要他持有该值就不需要担心它。
如果您将函数设计为 return 对传递的参数值的引用 - 您需要确保该值在与传递的参数相同的生命周期内使用(引用是 returned)
所以考虑:
StringOrRef func(strging const& a, string const& b);
...
StringOrRef val;
{ // begin scope:
SomeStruct s = get_defaul();
val = func("some value", s.get_ref_to_internal_string());
}// end of val scope
val; // is in scope but may be referencing freed data.
这里的问题是临时对象SomeStruct s
。如果它的成员函数 get_ref_to_internal_string() -> string&
return 是对该对象的字符串字段的引用(这通常是它的实现方式) - 那么当 s
超出范围时 - 引用变得无效.也就是说 - 它正在引用可能已分配给其他一些对象的已释放内存。
如果您在 val
中捕获该引用 - val 将引用无效数据。
如果一切都以 access violation
或信号结尾,您将很幸运。在最坏的情况下,您的程序会继续运行,但会随机崩溃。
我不是 100% 确定我理解要求的组合,但是:
#include <iostream>
#include <string>
#include <type_traits>
// if called with an rvalue (xvalue) as 2:nd arg, move or copy
std::string get_or_default(const std::string& st, std::string&& default_st) {
std::cout << "got temporary\n";
if(st.empty())
return std::move(default_st); // rval, move ctor
// return std::forward<std::string>(default_st); // alternative
else
return st; // lval, copy ctor
}
// lvalue as 2:nd argument, return the reference as-is
const std::string& get_or_default(const std::string& st,
const std::string& default_st) {
std::cout << "got ref\n";
if(st.empty()) return default_st;
else return st;
}
int main() {
std::string lval = "lval";
// get ref or copy ...
decltype(auto) s1 = get_or_default("", "temporary1");
decltype(auto) s2 = get_or_default("", std::string("temporary2"));
decltype(auto) s3 = get_or_default("", lval);
std::cout << std::boolalpha;
std::cout << std::is_reference_v<decltype(s1)> << "\n";
std::cout << std::is_reference_v<decltype(s2)> << "\n";
std::cout << std::is_reference_v<decltype(s3)> << "\n";
}
输出:
got temporary
got temporary
got ref
false
false
true
编辑:在 OP:s 测试后制作了一个稍微更通用的版本。它可以使用 lambda,例如
auto empty_check = [](const std::string& s) { return s.empty(); };
测试第一个参数是否为空。
template<typename T, typename F>
T get_or_default(const T& st, T&& default_st, F empty) {
if(empty(st)) return std::move(default_st);
// return std::forward<T>(default_st); // alternative
else return st;
}
template<typename T, typename F>
const T& get_or_default(const T& st, const T& default_st, F empty) {
if(empty(st)) return default_st;
else return st;
}
我正在尝试编写一个函数,它可以 return 对作为第一个参数传递的现有对象的引用(如果它处于正确状态)或创建并 return 一个新的使用文字作为第二个参数传递的对象(默认)。
如果一个函数不仅可以接受字面量,还可以接受另一个现有对象作为第二个(默认)参数和 return 对它的引用,那就更好了。
下面是一个简单的实现,但它做了很多不需要的工作:
如果使用左值作为第二个(默认)参数调用,它会调用为 return 选择的参数的复制构造函数。理想情况下,对对象的引用应该 returned.
如果使用文字作为第二个(默认)参数调用,它会调用构造函数、复制构造函数和析构函数,即使没有为 return 选择第二个(默认)参数。如果在不调用复制构造函数或析构函数的情况下构造对象并将其 returned 作为右值引用会更好。
std::string get_or_default(const std::string& st, const std::string& default_st) {
if (st.empty()) return default_st
else return st;
}
有没有一种方法可以更有效地完成此操作,同时仍然对调用者保持简单?如果我是正确的,这需要一个函数来根据函数内部做出的 运行 时间决定来更改 return 类型,但我想不出一个简单的调用者解决方案。
好吧,这里有一些东西。
要直接表达您的要求,您可以使用 std::variant<std::string, std::string&>
之类的东西作为您的函数 return 类型。虽然我没有检查变体是否可以存储引用。
或者来自第三方库的一些等价物。要么<>?
您还可以编写自己的 class 包装字符串和字符串引用。
(不是真实代码)
struct StringOrRef {
enum class Type {Value, Ref} type;
union {
std::string value;
std::reference_wrapper<const std::string> ref;
};
...
};
检查主题:C++ 中的判别联合。
但我认为你的例子有更大的问题! 请考虑数据的所有权。 std::string 获取传递的数据的所有权。这就是它复制数据的原因。因此,当你的函数 returns - 被调用者确定它有一个数据并且只要他持有该值就不需要担心它。
如果您将函数设计为 return 对传递的参数值的引用 - 您需要确保该值在与传递的参数相同的生命周期内使用(引用是 returned)
所以考虑:
StringOrRef func(strging const& a, string const& b);
...
StringOrRef val;
{ // begin scope:
SomeStruct s = get_defaul();
val = func("some value", s.get_ref_to_internal_string());
}// end of val scope
val; // is in scope but may be referencing freed data.
这里的问题是临时对象SomeStruct s
。如果它的成员函数 get_ref_to_internal_string() -> string&
return 是对该对象的字符串字段的引用(这通常是它的实现方式) - 那么当 s
超出范围时 - 引用变得无效.也就是说 - 它正在引用可能已分配给其他一些对象的已释放内存。
如果您在 val
中捕获该引用 - val 将引用无效数据。
如果一切都以 access violation
或信号结尾,您将很幸运。在最坏的情况下,您的程序会继续运行,但会随机崩溃。
我不是 100% 确定我理解要求的组合,但是:
#include <iostream>
#include <string>
#include <type_traits>
// if called with an rvalue (xvalue) as 2:nd arg, move or copy
std::string get_or_default(const std::string& st, std::string&& default_st) {
std::cout << "got temporary\n";
if(st.empty())
return std::move(default_st); // rval, move ctor
// return std::forward<std::string>(default_st); // alternative
else
return st; // lval, copy ctor
}
// lvalue as 2:nd argument, return the reference as-is
const std::string& get_or_default(const std::string& st,
const std::string& default_st) {
std::cout << "got ref\n";
if(st.empty()) return default_st;
else return st;
}
int main() {
std::string lval = "lval";
// get ref or copy ...
decltype(auto) s1 = get_or_default("", "temporary1");
decltype(auto) s2 = get_or_default("", std::string("temporary2"));
decltype(auto) s3 = get_or_default("", lval);
std::cout << std::boolalpha;
std::cout << std::is_reference_v<decltype(s1)> << "\n";
std::cout << std::is_reference_v<decltype(s2)> << "\n";
std::cout << std::is_reference_v<decltype(s3)> << "\n";
}
输出:
got temporary
got temporary
got ref
false
false
true
编辑:在 OP:s 测试后制作了一个稍微更通用的版本。它可以使用 lambda,例如
auto empty_check = [](const std::string& s) { return s.empty(); };
测试第一个参数是否为空。
template<typename T, typename F>
T get_or_default(const T& st, T&& default_st, F empty) {
if(empty(st)) return std::move(default_st);
// return std::forward<T>(default_st); // alternative
else return st;
}
template<typename T, typename F>
const T& get_or_default(const T& st, const T& default_st, F empty) {
if(empty(st)) return default_st;
else return st;
}