<C++> Función contenedora C++11 y enlace

1. Envoltura de funciones

En C ++ 11, std::functionlos contenedores de funciones se pueden crear usando std::functionEs un objeto de función general que puede almacenar, copiar y llamar a varios objetos a los que se puede llamar, incluidos punteros de función, objetos de función, expresiones lambda, etc. functionLos envoltorios también se conocen como adaptadores. functionLa esencia es una plantilla de clase y un contenedor.

ret = func(x);

func¿ Qué puede haber ahí arriba ? Entonces, func¿tal vez el nombre de la función? puntero de función? ¿Objetos de función (objetos funtores)? ¿También tal vez un objeto de expresión Lambda? ¡Así que estos son tipos invocables! ¡Tales tipos ricos pueden conducir a plantillas ineficientes! ¿por qué? sigamos leyendo

template<class F, class T>
T useF(F f, T x) {
    
    
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}

double f(double i) {
    
    
    return i / 2;
}

struct Functor {
    
    
    double operator()(double d) {
    
    
        return d / 3;
    }
};

int main() {
    
    // 函数名
    cout << useF(f, 11.11) << endl;
    // 函数对象
    cout << useF(Functor(), 11.11) << endl;
    // lamber表达式
    cout << useF([](double d) -> double {
    
     return d / 4; }, 11.11) << endl;
    return 0;
}

La salida es la siguiente:

count:1
count:00EDC140
5.555
count:1
count:00EDC144
3.70333
count:1
count:00EDC148
2.7775

Se puede encontrar que las direcciones de los tres recuentos son diferentes, por lo que se puede encontrar que useFla plantilla de función se ha instanciado tres veces.

Cómo usar la función

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参
#include <functional>
int f(int a, int b){
    
    
	return a + b;
}

struct Functor{
    
    
public:
	int operator() (int a, int b){
    
    
		return a + b;
	}
};

class Plus{
    
    
public:
	static int plusi(int a, int b){
    
    
		return a + b;
	}
	double plusd(double a, double b){
    
    
		return a + b;
	}
};

1. Puntero de función

// 函数名(函数指针)
std::function<int(int, int)> func1 = f;
cout << func1(1, 2) << endl;

En este ejemplo, creamos un envoltorio de función fpasando un puntero de función al constructor de . Dado que es un puntero de función, su tipo coincide, toma dos argumentos de tipo y devuelve un valor de tipo. Podemos llamar a la función envuelta y pasar parámetros.std::functionfunc1fstd::function<int(int, int)>intintfunc1

2. Objeto de función

// 函数对象
std::function<int(int, int)> func2 = Functor();
cout << func2(1, 2) << endl;

En este ejemplo, creamos una Functorinstancia del objeto de función y se la pasamos al std::functionconstructor de la función para crear el contenedor de función func2. Dado que Functores un objeto invocable (que define operator()la función), su tipo std::function<int(int, int)>coincide con . Podemos func2llamar al objeto de función envuelto y pasar parámetros.

3. Expresiones lambda

// Lambda表达式
std::function<int(int, int)> func3 = [](const int a, const int b) {
    
     return a + b; };
cout << func3(1, 2) << endl;

En este ejemplo, creamos una función anónima usando una expresión Lambda y la pasamos al std::functionconstructor de la función para crear el contenedor de función func3. El compilador deduce el tipo de la expresión lambda y std::function<int(int, int)>coincide con . Podemos func3llamar a la función Lambda envuelta con y pasar parámetros.

4. Funciones miembro estáticas de una clase

// 类的静态成员函数
std::function<int(int, int)> func4 = &Plus::plusi;
cout << func4(1, 2) << endl;

En este ejemplo, usamos &Plus::plusipara obtener un puntero a Plusla función miembro estática de una clase plusiy pasarlo al std::functionconstructor para crear un contenedor de función func4. Las funciones miembro estáticas no dependen de una instancia de la clase, por lo que no se requiere ningún parámetro de objeto adicional. Podemos func4llamar a la función de miembro estático envuelto y pasar parámetros.

5. Funciones miembro ordinarias de una clase

//类的普通成员函数
std::function<double(Plus, double, double)> func5 = &Plus::plusd;
cout << func5(Plus(), 1.1, 2.2) << endl;

En este ejemplo, creamos contenedores de función &Plus::plusdpara obtener un puntero a Plusuna función miembro normal de la clase plusdy pasarlo al constructor de la clase . Las funciones miembro ordinarias dependen de la instancia de la clase , por lo que necesitamos pasar un objeto de clase adicional como parámetro . Podemos llamar a la función miembro ordinaria envuelta a través de y pasar el objeto de clase y otros parámetros.std::functionfunc5func5

Con un contenedor, ¿cómo resolver el problema de la baja eficiencia de las plantillas y la instanciación de múltiples copias?

#include <functional>
#include <iostream>
using namespace std;

template<class F, class T>
T useF(F f, T x) {
    
    
    static int count = 0;
    cout << "count:" << ++count << endl;
    cout << "count:" << &count << endl;
    return f(x);
}

double f(double i) {
    
    
    return i / 2;
}

struct Functor {
    
    
    double operator()(double d) {
    
    
        return d / 3;
    }
};

int main() {
    
    // 函数名
    std::function<double(double)> func1 = f;
    cout << useF(func1, 11.11) << endl;
    // 函数对象
    std::function<double(double)> func2 = Functor();
    cout << useF(func2, 11.11) << endl;
    // Lambda表达式
    std::function<double(double)> func3 = [](double d) -> double {
    
     return d / 4; };
    cout << useF(func3, 11.11) << endl;
    return 0;
}

Resultado de salida:

count:1
count:0071E4C8
5.555
count:2
count:0071E4C8
3.70333
count:3
count:0071E4C8
2.7775

Como resultado, se puede encontrar que las tres direcciones de conteo son las mismas y solo functionse instancia una copia.

Algunos otros escenarios para envoltorios:

150. Evaluación de la expresión polaca inversa - LeetCode

Usando la solución anterior del contenedor:

class Solution {
    
    
public:
    int evalRPN(vector<string> &tokens) {
    
    
        stack<int> st;
        for (auto &str: tokens) {
    
    
            if (str == "+" || str == "-" || str == "*" || str == "/") {
    
    
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();
                switch (str[0]) {
    
    
                    case '+':
                        st.push(left + right);
                        break;
                    case '-':
                        st.push(left - right);
                        break;
                    case '*':
                        st.push(left * right);
                        break;
                    case '/':
                        st.push(left / right);
                        break;
                }
            } else {
    
    
                // 1、atoi itoa
                // 2、sprintf scanf
                // 3、stoi to_string C++11
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

Una solución usando un contenedor:

class Solution {
    
    
public:
    int evalRPN(vector<string> &tokens) {
    
    
        stack<long> st;
        unordered_map<string, function<long(long, long)>> opFuncMap = {
    
    
                {
    
    "+", [](long x, long y) -> long {
    
     return x + y; }},
                {
    
    "-", [](long x, long y) -> long {
    
     return x - y; }},
                {
    
    "*", [](long x, long y) -> long {
    
     return x * y; }},
                {
    
    "/", [](long x, long y) -> long {
    
     return x / y; }}};

        for (auto &str: tokens) {
    
    
            if (opFuncMap.find(str) != opFuncMap.end()) {
    
    
                long right = st.top();
                st.pop();
                long left = st.top();
                st.pop();

                st.push(opFuncMap[str](left, right));
            } else {
    
    
                st.push(stoll(str));
            }
        }

        return st.top();
    }
};

2. enlazar

std::bindes una plantilla de función en la biblioteca estándar de C++, ubicada <functional>en el archivo de encabezado. Se utiliza para crear un objeto de función (también conocido como enlazador de funciones), que puede vincular un objeto invocable con sus parámetros para formar un nuevo objeto invocable.

El prototipo es el siguiente:

template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

Piense en binduna función como un adaptador de función genérico que toma un objeto invocable y produce un nuevo objeto invocable que "se ajusta" a la lista de argumentos del objeto original. La forma general de llamar a bind:auto newCallable = bind(callable,arg_list);

donde newCallablesí mismo es un objeto invocable y arg_listes una lista separada por comas de argumentos correspondientes a los callableargumentos dados. Cuando llamemos newCallable, newCallableserá llamado callabley pasado a arg_listlos parámetros en él.

arg_listLos parámetros en pueden contener nombres de la forma n, donde n es un número entero, y estos parámetros son "marcadores de posición", que denotan newCallableparámetros que ocupan newCallablela "posición" de los parámetros pasados ​​a . El valor n indica la posición del parámetro en el objeto invocable resultante: 1 para el newCallableprimer parámetro de _2 para el segundo parámetro y así sucesivamente.

1. Puntero de función de enlace

#include <functional>
#include <iostream>

void foo(int a, int b) {
    
    
    std::cout << "Sum: " << (a + b) << std::endl;
}

int main() {
    
    
    auto boundFunc = std::bind(foo, 10, 20);
    boundFunc(); // 调用绑定的函数指针
    return 0;
}

std::bindVincula el puntero de función foocon los parámetros 10y 20crea un nuevo objeto de función boundFunc. La llamada boundFunc()invoca la función original foo, pasando los parámetros enlazados. La salida es "Suma: 30".

2. Funciones miembro vinculantes

#include <functional>
#include <iostream>

class MyClass {
    
    
public:
    void printSum(int a, int b) {
    
    
        std::cout << "Sum: " << (a + b) << std::endl;
    }
};

int main() {
    
    
    MyClass obj;
    auto boundFunc = std::bind(&MyClass::printSum, &obj, 10, 20);
    boundFunc(); // 调用绑定的成员函数
    return 0;
}

std::bindAl vincular la función miembro printSumcon el puntero del objeto &objy los parámetros 10, 20se crea un nuevo objeto de función boundFunc. Call boundFunc()llama a la función miembro original printSum, pasando el objeto enlazado y los parámetros. La salida es "Suma: 30".

3. Funciones de miembros vinculantes y marcadores de posición

#include <functional>
#include <iostream>

class MyClass {
    
    
public:
    void printSum(int a, int b) {
    
    
        std::cout << "Sum: " << (a + b) << std::endl;
    }
};

int main() {
    
    
    MyClass obj;
    auto boundFunc = std::bind(&MyClass::printSum, &obj, std::placeholders::_1, 20);
    boundFunc(10); // 调用绑定的成员函数,传递部分参数
    return 0;
}

std::bindVincular la función miembro printSumcon el puntero del objeto &objy un marcador de posición std::placeholders::_1(para el primer parámetro) y la constante 20crea un nuevo objeto de función boundFunc. Call boundFunc(10)llama a la función miembro original printSum, pasándola 10como primer argumento y la constante enlazada 20como segundo argumento. La salida es "Suma: 30".

4. Reordenación de parámetros y parámetros predeterminados

#include <functional>
#include <iostream>

void foo(int a, int b, int c) {
    
    
    std::cout << "Sum: " << (a + b * c) << std::endl;   //230
}

int main() {
    
    
    auto boundFunc = std::bind(foo, std::placeholders::_3, std::placeholders::_1, std::placeholders::_2);
    boundFunc(10, 20, 30); // 调用绑定的函数,重排参数顺序    
    return 0;
}

std::bindVincular la función foocon los marcadores de posición y std::placeholders::_3crea un nuevo objeto de función . La llamada llamará a la función original y los parámetros se pasan en el orden reorganizado, a está vinculado al tercer parámetro 30, b está vinculado al primer parámetro 10 y c está vinculado al segundo parámetro 20. Eso es 30+10*20 = 230.std::placeholders::_1std::placeholders::_2boundFuncboundFunc(10, 20, 30)foo

std::bindTambién admite configuraciones predeterminadas para parámetros vinculados, por ejemplo std::bind(foo, _1, _2, 100), donde 100se usa una constante fija como parámetro predeterminado.

Usar la función y unir

Ejemplo de uso

#include <functional>
#include <iostream>
#include <map>
#include <string>
using namespace std;
using namespace placeholders;

int Plus(int a, int b) {
    
    
    return a + b;
}

int Mul(int a, int b, int c, double rate) {
    
    
    return a + b + c * rate;
}

class Sub {
    
    
public:
    int sub(int a, int b) {
    
    
        return a - b;
    }
};

int main() {
    
    
    // 调整顺序 -- 鸡肋
    // _1 _2.... 定义在placeholders命名空间中,代表绑定函数对象的形参,
    // _1,_2...分别代表第一个形参、第二个形参...
    function<int(int, int)> funcPlus = Plus;
    //function<int(Sub, int, int)> funcSub = &Sub::sub;
    function<int(int, int)> funcSub = bind(&Sub::sub, Sub(), _2, _1);
    function<int(int, int, int)> funcMul = bind(Mul, _3, _1, _2, 1.5);

    cout << funcPlus(1, 2) << endl;  //3
    cout << funcSub(1, 2) << endl;   //1
    cout << funcMul(3, 2, 1) << endl;//a = 1, b = 3 c = 2 * 1.5   1+3+2*1.5=7
 
    return 0;
}
int main() {
    
    
    map<string, function<int(int, int)>> opFuncMap = {
    
    
            {
    
    "+", Plus},
            {
    
    "-", bind(&Sub::sub, Sub(), _1, _2)},
            {
    
    "*", bind(Mul, _2, _1, 1, 2)},//这里只能有两个占位符,function的参数只有2个
    };


    cout << opFuncMap["+"](1, 2) << endl;//3
    cout << opFuncMap["-"](1, 2) << endl;//-1
    cout << opFuncMap["*"](2, 3) << endl;//2+3+1*2=7
}

Supongo que te gusta

Origin blog.csdn.net/ikun66666/article/details/131367399
Recomendado
Clasificación