C26415 SMART_PTR_NOT_NEEDED

"Smart pointer parameter is used only to access contained pointer. Use T* or T& instead."

C++ Core Guidelines: R.30: Take smart pointers as parameters only to explicitly express lifetime semantics

Using a smart pointer type to pass data to a function indicates that the target function needs to manage the lifetime of the contained object. However, if the function only uses the smart pointer to access the contained object and never actually calls any code that may lead to its deallocation (that is, never affect its lifetime), there is usually no need to complicate the interface with smart pointers. A plain pointer or reference to the contained object is preferred.

Remarks

This check covers a majority of scenarios that also cause C26410, C26415, C26417, and C26418. It is better to clean up SMART_PTR_NOT_NEEDED first and then switch to edge cases for shared or unique pointers. For more focused cleanup, this warning can be disabled.

In addition to the standard std::unqiue_pointer and std::shared_pointer templates, this check recognizes user-defined types that are likely intended to be smart pointers. Such types are expected to define the following operations:

  • Overloaded dereference or member access operators that are public and not marked as deleted.
  • Public destructor that's not deleted or defaulted. This includes destructors that are explicitly defined empty.

Interpretation of the operations that can affect the lifetime of contained objects is broad and includes:

  • Any function that accepts a pointer or reference parameter to a non-constant smart pointer
  • Copy or move constructors or assignment operators
  • Non-constant functions

Example

Cumbersome lifetime management.

bool set_initial_message(
            const std::unique_ptr<message> &m) // C26415, also C26410 NO_REF_TO_CONST_UNIQUE_PTR
{
    if (!m || initial_message_)
        return false;

    initial_message_.reset(m.get());
    return true;
}

void pass_message(const message_info &info)
{
    auto m = std::make_unique<message>(info);
    const auto release = set_initial_message(m);
    // ...
    if (release)
        m.release();
}

Example

Cumbersome lifetime management - reworked.

void set_initial_message(std::shared_ptr<message> m) noexcept
{
    if (m && !initial_message_)
        initial_message_ = std::move(m);
}

void pass_message(const message_info &info)
{
    auto m = std::make_shared<message>(info);
    set_initial_message(m);
    // ...
}