问题
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