mora
mora

Reputation: 2287

How can I let compiler to deduce a type of nullptr?

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

Answers (4)

Holt
Holt

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

Guillaume Racicot
Guillaume Racicot

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

feedc0de
feedc0de

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

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

Related Questions