¿Diferencia entre `const shared_ptr <T>` y `shared_ptr <const T>`?

116

Estoy escribiendo un método de acceso para un puntero compartido en C ++ que es algo como esto:

class Foo {
public:
    return_type getBar() const {
        return m_bar;
    }

private:
    boost::shared_ptr<Bar> m_bar;
}

Por lo tanto, para admitir la constancia del getBar()tipo de retorno debe haber un boost::shared_ptrque evite la modificación del Barque apunta. Mi conjetura es que shared_ptr<const Bar>es el tipo que quiero volver a hacer eso, mientras que const shared_ptr<Bar>impediría la reasignación del propio puntero al punto a otro Bar, sino permitir la modificación de la Barque apunte a ... Sin embargo, no estoy seguro. Le agradecería que alguien que esté seguro pudiera confirmar esto o corregirme si me equivoco. ¡Gracias!

Dave Lillethun
fuente
3
Es exactamente lo que dijiste. Puede consultar la documentación de los operadores *y ->confirmarlo.
syam
2
¿Cuál es la diferencia entre T *consty T const *? Lo mismo.
3
@ H2CO3 Para nada. El constnormalmente modifica lo que lo precede, por lo que T *constes un constpuntero Ty T const*es un puntero const T. Y es mejor evitar usar constnada antes.
James Kanze
6
@JamesKanze, ese es el punto de H2CO3: la diferencia entre T *consty T const *es la misma que la diferencia entre const shared_ptr<T>yshared_ptr<const T>
Jonathan Wakely
1
@JamesKanze Oh, pero sí. T *constes un puntero constante a no constante T, también lo es const shared_ptr<T>. Por el contrario, T const *es un puntero no constante a const T, también lo es shared_ptr<const T>.

Respuestas:

176

Tienes razón. shared_ptr<const T> p;es similar a const T * p;(o, de manera equivalente, T const * p;), es decir, el objeto apuntado es constmientras que const shared_ptr<T> p;es similar a lo T* const p;que significa que pes const. En resumen:

shared_ptr<T> p;             ---> T * p;                                    : nothing is const
const shared_ptr<T> p;       ---> T * const p;                              : p is const
shared_ptr<const T> p;       ---> const T * p;       <=> T const * p;       : *p is const
const shared_ptr<const T> p; ---> const T * const p; <=> T const * const p; : p and *p are const.

Lo mismo vale para weak_ptry unique_ptr.

Cassio Neri
fuente
1
También respondiste una pregunta que tenía en la parte de atrás de mi cabeza sobre los punteros regulares (const T * vs T * const vs T const *). :) No mencioné eso porque no quería que mi pregunta sobre SO fuera demasiado amplia, y esta era la pregunta pertinente a mi tarea actual. De todos modos, creo que ahora lo entiendo muy bien. ¡Gracias!
Dave Lillethun
9
Me alegro de que haya ayudado. Un último consejo que suelo recordar sobre const T* p;', 'T const * p;y T * const p. Vea *como un separador en el sentido de que lo que es constes lo que está en el mismo lado de *.
Cassio Neri
5
Mi regla general es que constsiempre se refiere a lo que está en el lado izquierdo. Si no hay nada a la izquierda, es lo que está al lado derecho.
hochl
hochi — qué tal const T * p; equivalente a T const * p ;?
Vlad
Cassio, puede agregar que en el caso del tipo devuelto const shared_ptr <T>, no se puede usar en funciones que no sean constantes mientras que esto no es cierto para punteros constantes.
Vlad
2

boost::shared_ptr<Bar const>evita la modificación del Barobjeto a través del puntero compartido. Como valor de retorno, la const in boost::shared_ptr<Bar> constsignifica que no puede llamar a una función no constante en el temporal devuelto; si fuera por un puntero real (por ejemplo Bar* const), sería completamente ignorado.

En general, incluso aquí, se aplican las reglas habituales: constmodifica lo que le precede: en boost::shared_ptr<Bar const>, el Bar; en boost::shared_ptr<Bar> const, es la instanciación (la expresión boost::shared_ptr<Bar>que es const.

James Kanze
fuente
1
@gatopeich Entonces puedes delete.
Marcin
@Marcin ¿podrías colaborar?
gatopeich
1
#Check this simple code to understand... copy-paste the below code to check on any c++11 compiler

#include <memory>
using namespace std;

class A {
    public:
        int a = 5;
};

shared_ptr<A> f1() {
    const shared_ptr<A> sA(new A);
    shared_ptr<A> sA2(new A);
    sA = sA2; // compile-error
    return sA;
}

shared_ptr<A> f2() {
    shared_ptr<const A> sA(new A);
    sA->a = 4; // compile-error
    return sA;
}

int main(int argc, char** argv) {
    f1();
    f2();
    return 0;
}
vivek2k6
fuente
Puedo sugerir el uso de std::make_shared()(desde C ++ 14).
Joel Bodenmann
0

Me gustaría una demostración simple basada en la respuesta de @Cassio Neri:

#include <memory>

int main(){
    std::shared_ptr<int> i = std::make_shared<int>(1);
    std::shared_ptr<int const> ci;

    // i = ci; // compile error
    ci = i;
    std::cout << *i << "\t" << *ci << std::endl; // both will be 1

    *i = 2;
    std::cout << *i << "\t" << *ci << std::endl; // both will be 2

    i = std::make_shared<int>(3);
    std::cout << *i << "\t" << *ci << std::endl; // only *i has changed

    // *ci = 20; // compile error
    ci = std::make_shared<int>(5);
    std::cout << *i << "\t" << *ci << std::endl; // only *ci has changed

}
Jónás Balázs
fuente