有没有办法从函数返回新对象或对现有对象的引用?

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

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

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

  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;
}

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

cairuojin 回答:有没有办法从函数返回新对象或对现有对象的引用?

我不确定我是否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;
}
,

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

(不是真实代码)

struct StringOrRef {
    enum class Type {Value,Ref} type;
    union {
        std::string value;
        std::reference_wrapper<const std::string> ref;
    };
...
};

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

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

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

所以请考虑:


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

本文链接:https://www.f2er.com/3148520.html

大家都在问