cpp::daily #Episódio002 - Ligações e Marcadores de Posição

std::bind e std::placeholders, 10 exemplos!


std::bind e std::placeholders

Muitas vezes precisamos implementar uma função sob demanda, ou seja, passar parâmetros de acordo com a necessidade do nosso aplicativo. E o std::placeholders servem para isso.

O namespace std::placeholders trabalha juntamente com a função std::bind e precisamos incluir o cabeçalho <funcional> para poder utilizarmos. Eles contém os objetos de placeholder [_1,..._N] onde N é um número máximo definido pela implementação.

O template de função std::bind retorna um objeto de função com base em fn, mas com seus argumentos vinculados a args.

Quando usado como um argumento em uma expressão std::bind, os objetos de espaço reservado são armazenados no objeto de função gerado e quando esse objeto de função é chamado com argumentos não acoplados, cada espaço reservado _N é substituído pelo enésimo argumento não acoplado correspondente.

Características das Ligações e marcadores de posição

  • Cada espaço reservado é declarado como : extern /* não especificada */ _1; // até o c++17;
  • As implementações são estimuladas a declarar os marcadores de posição como: inline constexpr /* não especificada */ _1;
  • Embora os declare por: extern /* não especificada */ _1; , ainda é permitido pelo padrão;
  • Os tipos de objetos de espaço reservado são DefaultConstructible e CopyConstructible
  • Seus construtores de cópia/movimentação padrão não lançam exceções;
  • Para qualquer espaço reservado _N, o tipo std::is_placeholder<decltype(_N)>;
  • É definido e é derivado de std::integral_constant<int, N>.

Exemplos

01. Usando o básico com parâmetro _1

Dada a função soma_sub(int, int, int) que retorna a soma e subtração dos parâmetros, respectivamente, se quisermos que um parâmetro seja dinâmico:

Terceiro parâmetro dinâmico

#include <iostream>
#include <functional> // para std::placeholders e std::bind

using namespace std::placeholders;

int soma_sub( int x, int y, int z ){
  return x + y - z;
}

int main( int argc , char **argv ){
  // substitui o  z
  auto fn = std::bind( soma_sub, 9, 1, _1 );
  std::cout << fn( 2 ) << '\n'; // equivale soma_sub( 9, 1, 2 ) = 8
  std::cout << fn( 3 ) << '\n'; // equivale soma_sub( 9, 1, 3 ) = 7

  return 0;
}

02. Segundo parâmetro dinâmico

Substitui o y

auto f2 = std::bind( soma_sub, 9, _1, 1 );
std::cout << f2( 2 ) << '\n'; // equivale soma_sub( 9, 2, 1 ) = 10
std::cout << f2( 3 ) << '\n'; // equivale soma_sub( 9, 3, 1 ) = 11

03. Primeiro parâmetro dinâmico

Substitui o x

auto f3 = std::bind( soma_sub, _1, 9, 1 );
std::cout << f3( 2 ) << '\n'; // equivale soma_sub( 2, 9, 1 ) = 10
std::cout << f3( 3 ) << '\n'; // equivale soma_sub( 3, 9, 2 ) = 11

04. Substituindo 2 parâmetros

Substitui o y e o z, respectivamente _1 e _2 . Como estamos usando o parâmetro _2, precisamos passar 2 parâmetros, caso contrário gera erro ao compilar.

auto f4 = std::bind( soma_sub, 1, _1, _2 );
std::cout << f4( 1, 2 ) << '\n'; // equivale soma_sub( 1, 1, 2 ) = 1 + 1 - 2 = 0
std::cout << f4( 3, 10 ) << '\n'; // equivale soma_sub( 1, 3, 10 ) = 1 + 3 - 10 = -6

05. Substituindo 2 parâmetros com alteração na ordem

Substitui o z e o y, respectivamente _2 e _1

auto f5 = std::bind( soma_sub, 1, _2, _1 );
std::cout << f5( 1, 2 ) << '\n'; // equivale soma_sub( 1, 2, 1 ) = 1 + 2 - 1 = 2
std::cout << f5( 3, 10 ) << '\n'; // equivale soma_sub( 1, 10, 3 ) = 1 + 10 - 3 = 8

06. Substituindo 2 parâmetros, mas alterando só o segundo

_2 = y , x = 1, z = 3 . Tem que passar 2 parâmetros(senão não compila), mas o 1º será ignorado!

auto f6 = std::bind( soma_sub, 1, _2, 3 );
//                             |      |_________________________________
//                             |                                       |
//                             |__________________________________     |
//                                                               |     |
//            ignorado                                           |     | 
//               ↓                                               |     |
std::cout << f6( 897, 0/* _2 */ ) << '\n'; // equivale soma_sub( 1, 0, 3 ) = 1 + 0 - 3 = -2

//            ignorado   
//               ↓
std::cout << f6( 800, 2/* _2 */ ) << '\n';// equivale soma_sub( 1, 2, 0 ) = 1 + 2 - 0 = 2

07. Substituindo só o 3º parâmetro

Pra entender de vez! x = _3, precisa informar 3 parâmetros(senão não compila), pois está usando o _3, mas os dois primeiros serão ignorados.

auto f7 = std::bind( soma_sub, _3, 1, 3 );
std::cout << f7( 0, 0, 30 ) << '\n'; // z = 8 , equivale soma_sub( 1, 3, 8 ) = 30 + 1 - 3 = 28

08. Usando alias

Nova função de nome show_name( std::string & )

#include <iostream>
#include <functional>

namespace pl = std::placeholders;

void show_name( std::string &name ){
  std::cout << name << '\n';
}

int main( int argc , char **argv ){

  std::string name("Olá, marcadores de posição");
  auto fn1 = std::bind( show_name, pl::_1 );
  fn1( name );

  return 0;
}

09. Sem usar o auto

std::function<void( std::string & )> fn2 = std::bind( show_name , pl::_1 );
name = "Like, a boss!"; // declarada e inicializada no exemplo anterior!
fn2( name );

10. Sem indicar namespace

Exemplo final sem explicações, exercício!

#include <iostream>
#include <functional>

int add3(int x1, int x2, int x3) {
  return x1 + x2 + x3;
}

int main( int argc , char **argv ){
  auto fadd3 = std::bind(add3, 11, std::placeholders::_1, std::placeholders::_2);
  std::cout << fadd3(22, 33) << '\n';
  return 0;
}

Curiosidades: Se você o std::bind puro, ele pode lhe dar um resultado incorreto. Outra coisa também é se você usa o bind da lib boost: boost::bind não é compatível com a std::bind.

Acompanhe o cpp::daily

Deseja aprender C++ e criar seus programas Gráficos e para Terminal com portabilidade para Linux e Windows?

Então se inscreva nos nossos Cursos de C++ Moderno . Você aprender criar:

Acesse o endereço:

https://terminalroot.com.br/cpp/ e saiba mais!

Links úteis


cppdaily cpp


Compartilhe


Nosso canal no Youtube

Inscreva-se


Marcos Oliveira

Marcos Oliveira

Desenvolvedor de software
https://github.com/terroo


Crie Aplicativos Gráficos para Linux e Windows com C++

Aprenda C++ Moderno e crie Games, Programas CLI, GUI e TUI de forma fácil.

Saiba Mais

Receba as novidades no seu e-mail!

Após cadastro e confirmação do e-mail, enviaremos semanalmente resumos e também sempre que houver novidades por aqui para que você mantenha-se atualizado!