Main Content

AUTOSAR C++14 Rule A8-4-13

A std::shared_ptr shall be passed to a function as: (1) a copy to express the function shares ownership (2) an lvalue reference to express that the function replaces the managed object (3) a const lvalue reference to express that the function retains a reference count.

Since R2022b

Description

Rule Definition

A std::shared_ptr shall be passed to a function as: (1) a copy to express the function shares ownership (2) an lvalue reference to express that the function replaces the managed object (3) a const lvalue reference to express that the function retains a reference count.

Rationale

You use an std::shared_ptr smart pointer to indicate that more than one smart pointer manages and shares ownership of the same object. A smart pointer manages the lifetime of a dynamically allocated object and destroys that object when the smart pointer goes out of scope. Pass an std::shared_ptr smart pointer to a function in one of these ways:

  • If you expect the function to share ownership of the managed object, pass the smart pointer by value. The std::shared_ptr reference count is incremented at the start of the function and decremented at the end of the function. For instance, smartPtr is passed to func() in this code and that func() shares ownership of the managed int object until the function ends.

    void func(std::shared_ptr<int> smartPtr) {
      //Shared ownership of managed object
      //smartPtr until end of func
    }

  • If you expect the function to replace the object that the smart pointer manages on at least one code path, pass the smart pointer by lvalue reference. For instance, you replace the managed object by calling operator= or std::shared_ptr member function reset().

    For example, the lvalue reference parameter of func() is reset inside the function and manages a new int object in this code. The function does not share ownership of the original managed object.

    void func(std::shared_ptr<int>& smartPtr) {
      smartPtr.reset(new int(1));
    }
    If you do not intend to replace the managed object on at least one code path, pass an lvalue reference to the managed object itself (void func(int& obj);).

  • If you expect that the function might share ownership of the managed object and copy the parameter to another std::shared_ptr smart pointer on at least one execution path, pass the smart pointer by const lvalue reference. Passing by const lvalue reference avoids unnecessary copies on the other execution paths.

    For instance, the parameter smartPtr is not compliant because it is not copied to another std::shared_ptr on any execution path in this code. If you do not intend to copy the parameter to another std::shared_ptr smart pointer, pass the parameter by const lvalue reference to the managed object itself.

    #include <iostream>
    #include <memory>
    
    void func(const std::shared_ptr<int>& smartPtr) { //Non-compliant
      std::cout << *smartPtr << std::endl;
    }
    

Polyspace Implementation

Polyspace® reports the use of an std::shared_ptr smart pointer parameter passed as an lvalue reference if that parameter is not reset.

Polyspace considers that a parameter is reset in these cases:

  • The parameter is used as the destination of a copy or move assignment.

  • The parameter is moved-from through a call to std::move.

  • The parameter is used as an argument to std::shared_ptr member functions reset() or swap().

  • The parameter is used as the argument of any function that takes a non-const qualified lvalue reference to an std::shared_ptr smart pointer, regardless of the implementation of the callee.

Polyspace also reports the use of an std::shared_ptr pointer parameter passed as a const lvalue reference if that parameter is not copied to another std::shared_ptr pointer along at least one execution path.

The parameter is copied to another std::shared_ptr smart pointer if it is used as the argument of one of these functions that take a const lvalue reference to std::shared_ptr as a parameter:

  • std::static_pointer_cast

  • std::dynamic_pointer_cast

  • std::const_pointer_cast

  • std::reinterpret_pointer_cast

If you do not intend to copy the parameter to another shared_ptr smart pointer, pass a const lvalue reference to the managed object instead.

Polyspace does not report the use of smart pointers as parameter types in these cases:

  • The pointer is passed as an rvalue reference parameter to a function and the pointer is effectively moved-from inside that function.

  • The pointer is used as a parameter of a template function. The parameter type of a template function depends on the instantiation of that function and a fix is not always possible at the template design level. For instance, Polyspace does not report the use of shared pointer ptr in this code.

    #include <iostream>
    #include <memory>
    #include <cassert>
    
    template <typename T>
    double XtimesY(T& ptr) // Use of ptr not flagged
    {
      return (ptr->x) * (ptr->y);
      // ...
    }
    struct S
    {
      double x;
      double y;
      S(double x0, double y0) : x{x0}, y{y0} {}
    };
    void func()
    {
      auto a = std::make_shared<S>{100.0, 200.0};
      auto b = new S(100.0, 200.0);
      assert(XtimesY(a) == XtimesY(b));
    }

  • The smart pointer parameter is captured by a lambda function inside the function body. For example, Polyspace does not report the use of the lvalue reference to a shared pointer ptr as a parameter of func() because a lambda function captures the parameter inside the body of func(). Polyspace reports the use of the lambda function parameter up1 because its lifetime is not modified inside the lambda function.

    #include <memory>
    
    struct S
    {
      double x;
      double y;
      S(double x0, double y0) : x{x0}, y{y0} {}
    };
    
    void func(std::shared_ptr<S>& ptr)
    { // no defect is detected on 'ptr'
      auto lambdaF =
          [&ptr0 = ptr]            // 'ptr' is captured (by reference)
          (std::shared<S> up1) // Non-compliant
      {                            // lifetime of 'up1' not affected
        // ...
      };
      auto b = std::make_shared<S>(100.0, 200.0);
      lambdaF(std::move(b));
    }

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

#include <iostream>
#include <memory>

void func1(
    std::shared_ptr<int>& smartPtr,      //Non-compliant
    const std::shared_ptr<int>& constSmartPtr//Non-compliant
)
{
  std::cout << smartPtr; 
  auto deleter = std::get_deleter<void (*)(int *)>(constSmartPtr);
}
void func2(std::shared_ptr<int>& smartPtr){ //Non-compliant
  ++(*smartPtr); // Managed object not replaced
}

struct Base
{
  int a;
  virtual ~Base() {}
};
struct Derived : Base
{
  ~Derived() {}
};
void func3(
    std::shared_ptr<Base>& basePtr,            //Compliant
    const std::shared_ptr<Derived>& derivedPtr //Compliant
)
{
  //derivedPtr is copied to another shared_ptr
  //basePtr is the destination of a copy assignment
  basePtr = std::static_pointer_cast<Base>(derivedPtr);
}

In this example, Polyspace reports the use of the std::shared_ptr parameters that are passed to these functions:

  • func1() — The lvalue reference parameter smartPtr is not reset and the object managed by smartPtr is not replaced. Parameter constSmartPtr is noncompliant because the parameter is not copied to another shared pointer. The function get_deleter returns the deleter of the smart pointer, but does not create a new shared pointer.

  • func2() — The int object managed by the smart pointer is not replaced.

If you do not intend to modify the managed object of the lvalue reference parameters, pass an lvalue reference to the managed object instead. If you do not intend to copy the const lvalue reference parameter to another shared pointer, pass a const lvalue reference to the managed object instead.

Polyspace does not report the parameters of func3() because:

  • The lvalue reference parameter basePtr is the destination of a copy assignment.

  • The const lvalue reference parameter derivedPtr is copied to another shared pointer through the call to std::static_pointer_cast.

Check Information

Group: Declarators
Category: Required, Automated

Version History

Introduced in R2022b