是否可以通过函数返回新对象或对现有对象的引用?

问题描述 投票:0回答:2

我正在尝试编写一个函数,该函数可以返回对作为第一个参数传递的现有对象的引用(如果它处于正确状态),或者使用作为第二个参数传递的文字创建并返回一个新对象(默认) 。

如果一个函数不仅可以使用文字,还可以使用另一个现有对象作为第二个(默认)参数并返回对其的引用,那会更好。

下面是一个简单的实现,但是它做了很多不必要的工作:

  1. 如果以lvalue作为第二个(默认)参数调用,它将调用参数的副本构造函数,该函数被选择返回。理想情况下,应返回对对象的引用。

  2. 如果使用文字作为第二个(默认)参数调用,即使未选择第二个(默认)参数返回,它也会调用构造函数,复制构造函数和析构函数。如果构造一个对象并将其作为右值引用返回而不调用副本构造函数或析构函数,则更好。

std::string get_or_default(const std::string& st, const std::string& default_st) {
    if (st.empty()) return default_st
    else return st;
}

是否有一种方法可以更有效地完成此任务,同时仍然对呼叫者保持简单?如果我是正确的,那么这需要一个函数根据函数内部做出的运行时决策来更改返回类型,但是我无法想到针对调用者的简单解决方案。

c++ gcc parameter-passing c++17 return-by-reference
2个回答
0
投票

嗯,这里有几件事。为了直接表达您的要求,您可以使用std::variant<std::string, std::string&>之类的函数作为返回类型。虽然我还没有检查variant是否可以存储参考。或第三方库中的等效项。要么<>?您也可以编写自己的包装字符串和字符串引用的类。

(不是真实代码)

struct EitherEtring {
 enum class ValueType { Ref, Value } valueType;
enum {
std::string s;
std::String& v;
};
...

检查主题:区分C ++中的联合。

但是我认为您的示例存在更大的问题!请考虑数据的所有权。 std :: string获取所传递数据的所有权。这就是为什么它复制数据。因此,当您的函数返回时-被调用者可以确定它有数据,并且只要保存值就不必担心。

如果设计一个函数以返回对传递的参数值的引用,则需要确保该值在与传递的参数(返回引用的参数)相同的寿命内使用]

所以考虑:


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. 

这里的问题是临时对象SomeStructc s。如果它的成员函数get_ref_to_internal_string() -> string&返回对该对象的字符串字段的引用(通常是该对象的实现方式),则s超出范围时-ref无效。也就是说,它正在引用可能已提供给某些其他对象的已释放内存。并且,如果您在val中捕获了该引用,则val将引用无效数据。如果全部以access violation或信号结尾,您将很幸运。在最坏的情况下,您的程序会继续运行,但会随机崩溃。


0
投票

我不确定是否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
© www.soinside.com 2019 - 2024. All rights reserved.