entre diferencias c++ c++14

diferencias - c++17



std:: bind en un lambda genérico-auto deducción de tipo (1)

Considere el siguiente código:

#include <iostream> #include <functional> int main() { auto run = [](auto&& f, auto&& arg) { f(std::forward<decltype(arg)>(arg)); }; auto foo = [](int &x) {}; int var; auto run_foo = std::bind(run, foo, var); run_foo(); return 0; }

Lo que da el siguiente error de compilación cuando se compila con clang:

$ clang++ -std=c++14 my_test.cpp my_test.cpp:6:9: error: no matching function for call to object of type ''const (lambda at my_test.cpp:8:16)'' f(std::forward<decltype(arg)>(arg)); ^ /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:998:14: note: in instantiation of function template specialization ''main()::(anonymous class)::operator()<const (lambda at my_test.cpp:8:16) &, const int &>'' requested here = decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0), ^ /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/6.3.1/../../../../include/c++/6.3.1/functional:1003:2: note: in instantiation of default argument for ''operator()<>'' required here operator()(_Args&&... __args) const ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ my_test.cpp:11:12: note: while substituting deduced template arguments into function template ''operator()'' [with _Args = <>, _Result = (no value)] run_foo(); ^ my_test.cpp:8:16: note: candidate function not viable: 1st argument (''const int'') would lose const qualifier auto foo = [](int &x) {}; ^ my_test.cpp:8:16: note: conversion candidate of type ''void (*)(int &)'' 1 error generated.

¿Por qué se deduce que arg es const int& lugar de solo int& ?

std :: bind documentation dice:

Dado un objeto g obtenido de una llamada anterior a vincular, cuando se invoca en una expresión de llamada de función g (u1, u2, ... uM), tiene lugar una invocación del objeto almacenado, como por std :: invoke ( fd, std :: forward (v1), std :: forward (v2), ..., std :: forward (vN)), donde fd es un valor de tipo std :: decay_t los valores y tipos de los argumentos enlazados v1, v2, ..., vN se determinan como se especifica a continuación.

...

De lo contrario, el argumento almacenado ordinario arg se pasa al objeto invocable como argumento lvalue: el argumento vn en la llamada std :: invoke anterior es simplemente arg y el tipo correspondiente Vn es T cv &, donde cv es la misma cualificación cv que la de g.

Pero en este caso, run_foo es cv-no calificado . ¿Qué me estoy perdiendo?


MWE:

#include <functional> int main() { int i; std::bind([] (auto& x) {x = 1;}, i)(); }

[func.bind]/(10.4) indica que los calificadores cv del argumento pasado a la lambda son los del argumento a bind , aumentado por los calificadores cv de la envoltura de llamada; pero no hay ninguno, y por lo tanto se debe pasar una no- const int .

Tanto libc ++ como libstdc ++ no resuelven la llamada. Para libc ++, reportado como #32856 , libstdc ++ como #80564 . El problema principal es que ambas bibliotecas infieren el tipo de retorno en la firma de alguna manera, pareciendo esto para libstdc ++:

// Call as const template<typename... _Args, typename _Result = decltype( std::declval<typename enable_if<(sizeof...(_Args) >= 0), typename add_const<_Functor>::type&>::type>()( _Mu<_Bound_args>()( std::declval<const _Bound_args&>(), std::declval<tuple<_Args...>&>() )... ) )> _Result operator()(_Args&&... __args) const

Durante la deducción de los argumentos de la plantilla como lo requiere la resolución de sobrecarga, se creará una instancia del argumento de la plantilla predeterminada, lo que causa un error grave debido a nuestra asignación mal formada dentro del cierre.

Esto puede ser arreglado por un marcador de posición deducido: elimine _Result y su argumento predeterminado por completo, y declare el tipo de devolución como decltype(auto) . De esta manera, también nos deshacemos de SFINAE que influye en la resolución de sobrecarga y, por lo tanto, induce un comportamiento incorrecto:

#include <functional> #include <type_traits> struct A { template <typename T> std::enable_if_t<std::is_const<T>{}> operator()(T&) const; }; int main() { int i; std::bind(A{}, i)(); }

Esto no debe compilarse, como se explicó anteriormente, el argumento que se pasa a A::operator() debe ser const porque i y la envoltura de la llamada de reenvío son. Sin embargo, nuevamente, esto se compila bajo libc ++ y libstdc ++, porque sus operator() s retroceden en las versiones const después de que los no const fallan bajo SFINAE.