Create object only if some condition, otherwise return nullptr

半城伤御伤魂 提交于 2019-12-11 08:16:28

问题


I want to create an object only if some conditions are applied, otherwise retun nullptr. This is how I would do it in Delphi (2009+):

function GetGen(n : integer) : Generics.Collections.TList<Integer>;
var
i : integer;
begin
  result := nil;
  if n > 0 then begin
      result := Generics.Collections.TList<Integer>.Create;
      for i := 0 to n - 1 do result.Add(i);
  end;
end;

procedure TestGenList(n : integer);
var
   aInt : integer;
   aGen : Generics.Collections.TList<Integer>;
begin
   aGen := GetGen(n);
   if aGen = nil then begin
      WriteLn('No generic created!');
      Exit;
   end;
   WriteLn(Format('Size: %d', [aGen.Count]));
   for aInt in aGen do Write(Format('%d ', [aInt]));
   aGen.Free; //will clear integers
end;

procedure TestGen
begin
    TestGenList(0);
    Readln;
    TestGenList(5);
    Readln;
end.

This is how I could do it in C++ :

unique_ptr<vector<int>> GetUniquePrtVec(int n){
    if (n < 1) return(nullptr); //create only if correct input is given
    unique_ptr<vector<int>> result (new vector<int>);
    for (int i = 0 ; i != n; i++){
        result->push_back(i);
    }
    return(move(result));
}

void TestPtrVec(int n){
    unique_ptr<vector<int>> vec = GetUniquePrtVec(n);
    if (vec == nullptr){
        cout << "No vector created" << endl;
        return;
    }
    cout << endl << vec->size() << endl;
    for_each(vec->begin(), vec->end(), [](int n){cout << n << " " << endl;});
    vec->clear(); //clear vector
    vec.reset(nullptr);
}

void testVec3(){
    TestPtrVec(0);
    TestPtrVec(5);
}

My question is about the right idiom. Would you guys, experienced C++ programmers (for I am a beginner, just learning the language), do it this way? If not, then how would you do it?

Thanks.


回答1:


IMHO, the best way for your example, would be to simply return the std::vector by value and simply return an empty one if the input is invalid.

std::vector<int> get_vec(int n){
  std::vector<int> ret;
  for(unsigned i=0; i < n; ++i)
    ret.push_back(i);
  return ret; // will be empty for (n < 1)
              // and will be moved if (n >= 1)
}

One thing you need to learn: You don't need to explicitly std::move if you return a local variable. Just return by value. If copy elision is possible, it will do that (RVO / NRVO). If it can't for some reason, it'll first try to move it out before copying it. Note however, that a member of a local variable will not be moved automatically, aka

struct object{ std::vector<int> member; };

std::vector<int> foo(){
  object o;
  // ...
  return o.member; // no move, no copy elision, plain old copy
}

Now, your second function can also be improved and reduced:

void try_vec(int n){
  auto vec = get_vec(n); // will elide copy or simply move
  for(auto& x : vec) // will not loop if vector is empty
    std::cout << x << ' '; // why space and newline?
  std::cout << "\n"; // don't use std::endl, it also flushes the stream
}

And from your original function:

vec->clear(); //clear vector
vec.reset(nullptr);

Is not needed, that's the whole reason for smart pointers and resource managing containers. They will destroy what they own when they go out of scope.




回答2:


personally I believe that having a pointer to a vector is a bit necessary it looks as to me as if you could just return an empty vector or even throw an invalid argument error. The whole null return value is a bit of a hack and now you have to manage some memory because of it.

I personally would rather see

std::vector<int> get_vec(int n){
    std::vector<int> result;
    if(n < 1) return result;
    result.reserve(n);
    for (int i = 0 ; i != n; i++){
        result.push_back(i);
    }
    return result; 
}

or

std::vector<int> get_vec(int n){
    if(n < 1) throw std::invalid_argument("n must be greater than 1");
    std::vector<int> result;
    result.reserve(n);
    for (int i = 0 ; i != n; i++){
        result.push_back(i);
    }
    return result;
}


void test(int n){
    try{
    std::vector<int> vec = get_vec(n);
    catch(const std::exception& e)
    {
        std::cerr << "No vector created: " << e.what() << std::endl;
        return;
    }

//etc. .  .



回答3:


Seems what you need is something like boost::optional. Here is an example of its usage:

optional<char> get_async_input()
{
    if ( !queue.empty() )
        return optional<char>(queue.top());
    else return optional<char>(); // uninitialized
}

void receive_async_message()
{
    optional<char> rcv ;
    // The safe boolean conversion from 'rcv' is used here.
    while ( (rcv = get_async_input()) && !timeout() )
        output(*rcv);
}

For more information refer to boost documentation.




回答4:


Use exceptions or type erasure, returning NULL is the C way of doing things, not the C++ way.

Also you use the move semantic but you are not returning an r-value, it would not work like that.




回答5:


Im a little unfamilliar with this syntax, but I think it looks okay to me. Though, why not just use pointers with the usual c+ syntax?

vector<int> GetUniquePrtVec(int n)
{
    if (n < 1) 
        return null;

    vector<int>* result = new vector<int>;
    for (int i = 0 ; i != n; i++){
        result->push_back(i);
    }
    return (result);
}

Though Ive never used a vector pointer. Generally when I create a vector I pass it to a function by reference, like this:

vector<int> myVec;
bool bSuccess = PopulateVec(n, myVec);

vector<int>* PopulateVec(int inNum, vector<int>& inVec)
{
    if (inNum< 1)
        return false;

    for (int i = 0 ; i != inNum; i++)
    {
        inVec->push_back(i);
    }

    // inVec is "returned" by reference
    return true
}


来源:https://stackoverflow.com/questions/9635941/create-object-only-if-some-condition-otherwise-return-nullptr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!