Reputation: 2287
I am learning c++. I would like to let compiler to deduce nullptr as shared_ptr. Please read the following code,
struct A {};
struct B {
std::shared_ptr<A> a;
};
struct Tag {
std::shared_ptr<B> b;
};
auto GetSharedPtrClassB(Tag* tag) {
if (tag) {
auto& sharedB = *(tag->b);
return sharedB.a;
} else {
return nullptr; // Error : compiler cannot deduce type of nullptr.
}
}
In GetSharedPtrClassB
, nullptr
cannot be deduced as std::shared_ptr<A>
.
The error messages are following,
error: inconsistent deduction for ‘auto’: ‘std::shared_ptr<A>’ and then ‘std::nullptr_t’
How can I let compiler deduce nullptr as std::shared_ptr<A>
?
I can provide a type decltype(*(tag->b))
, but I cannot think of the next step to provide a type std::shared_ptr<A>
.
Thank you very much.
Upvotes: 4
Views: 1304
Reputation: 37606
Use a conditional operator to force the conversion from nullptr
to "whatever":
auto GetSharedPtrClassB(Tag* tag) {
return tag ? tag->b->a : nullptr;
}
Conversion from one operand to the other in a conditional operator is well-defined (see [expr.cond]), here the nullptr
gets converted to an object of type decltype(tag->b->a)
.
On the other hand, the rules for return type deduction when using auto
without a trailing return type are very strict - the deduced type must be the same for each return
statement ([dcl.spec.auto/9]):
If a function with a declared return type that contains a placeholder type has multiple return statements, the return type is deduced for each return statement. If the type deduced is not the same in each deduction, the program is ill-formed.
If your function cannot be reduced to a conditional operator, you can use a trailing return type:
auto GetSharedPtrClassB(Tag* tag) -> decltype(tag->b->a) {
if (tag) {
auto& sharedB = *(tag->b);
return sharedB.a;
} else {
return {};
}
}
Upvotes: 10
Reputation: 41750
I will assume that your code is actually use in a generic context. If your code don't use templates don't use decltype, specify types directly.
To deduce the return type, all return statements must evaluate to an expression of the same type. In your code, you have two return statements with two different types. One with std::shared_ptr<A>
, and one with std::nullptr_t
.
There is two solution: use the same types in both return statements, or explicitly define a return type.
Here's how to return the same type:
auto GetSharedPtrClassB(Tag* tag) {
if (tag) {
auto& sharedB = *(tag->b);
return sharedB.a;
}
// empty constructor same as initializing with `nullptr`
return decltype(tag->b->a){};
}
Or explicitly define a return type:
auto GetSharedPtrClassB(Tag* tag) -> decltype(tag->b->a) {
if (tag) {
auto& sharedB = *(tag->b);
return sharedB.a;
}
// empty constructor same as initializing with `nullptr`
return {};
}
By the way, in you code auto& sharedB = *(tag->b); return sharedB.a;
could be reduced to return tag->b->a;
Upvotes: 2
Reputation: 3796
You could use static_cast
like this:
auto GetSharedPtrClassB(Tag* tag) {
if (tag) {
auto& sharedB = *(tag->b);
return sharedB.a;
} else {
return static_cast<std::shared_ptr<A> >(nullptr);
}
}
Upvotes: 1
Reputation: 170044
You can return a default constructed shared_ptr instead.
auto GetSharedPtrClassB(Tag* tag) {
if (tag) {
auto& sharedB = *(tag->b);
return sharedB.a;
} else {
return std::shared_ptr<A>{};
}
}
Upvotes: 4