std :: función const corrección

11

Supongamos que tengo un tipo invocable como este:

struct mutable_callable
{
    int my_mutable = 0;
    int operator()() { // Not const
        return my_mutable++;
    }
};

Tenga en cuenta que mutable_callabletiene una no constante operator()que modifica una variable miembro .....

Ahora suponga que creo un std::functionfuera de mi tipo:

std::function<int()> foo = mutable_callable{};

Ahora puedo hacer esto:

void invoke(std::function<int()> const& z)
{
    z();
}

int main()
{
    invoke(foo); // foo changed.....oops
}

Ahora, por lo que puedo decir, std::functions operator()es constsegún: https://en.cppreference.com/w/cpp/utility/functional/function/operator ()

Así que mi intuición es que no deberías poder hacer esto .....

Pero luego mirando: https://en.cppreference.com/w/cpp/utility/functional/function/function

Esto no parece poner ninguna restricción sobre si el tipo invocable tiene o no una constante operator()......

Entonces mi pregunta es la siguiente: estoy en lo cierto al suponer que std::function<int()> const&es esencialmente lo mismo, ya std::function<int()>&que no existe una diferencia real entre el comportamiento de los dos ... y si ese es el caso, ¿por qué no es constcorrecto?

DarthRubik
fuente
@MaxLanghof No ..... std::functiontiene el equivalente a a struct a{ std::any x; };en él .....
DarthRubik
Aquí hay un pequeño fragmento de los aspectos internos de la std::functionimplementación de MSVC : i.stack.imgur.com/eNenN.png donde using _Ptrt = _Func_base<_Ret, _Types...>. Yo descanso mi caso.
Max Langhof

Respuestas:

3

Esto se reduce a lo mismo que struct A { int* x; };, donde const A a;puede modificar el valor de *(a.x)(pero no a dónde apunta). Hay un nivel de indirección en std::function(desde el tipo de borrado) a través del cual constno se propaga.

Y no, std::function<int()> const& fno tiene sentido. En un std::function<int()>& f, podría asignar un functor diferente f, lo que no puede hacer en el constcaso.

Max Langhof
fuente
Sí ... eso realmente tiene mucho sentido ... aunque todavía es confuso a primera vista
DarthRubik
Creo que es un defecto de diseño. Esta indirección debería ser un detalle de implementación transparente para el usuario, y la constante podría propagarse mediante alguna metaprogramación.
Igor R.
@IgorR. Sí, la constidad podría propagarse. std::vectorhace esto, std::unique_ptrno lo hace. Sin std::functionembargo, creo que no se trata realmente de expresar invariantes del estado functor. ¿Quizás podríamos reutilizar tipos de funciones abominables (es decir std::function<int() const>) para distinguir?
Max Langhof
unique_ptrno debe propagar la constidad, como no lo hace el puntero regular. Y std::function<int() const>no compilaría.
Igor R.
@IgorR. Lo sé. Mi punto era que algunas partes de la biblioteca estándar lo hacen y otras no. En qué categoría std::functiondebería caer no me queda claro. Y esto std::function<int() const>era hipotético, por supuesto, no se compila ahora, pero ¿satisfaría, por ejemplo, el OP aquí si eso pudiera hacerse válido, expresando "solo se pueden asignar functores con un operator() const(o apátridas)"? (incluso si detrás de escena sería bastante atroz, debido al uso de tipos de funciones abominables)?
Max Langhof