Argumento padrão - Default argument

Em programação de computador , um argumento padrão é um argumento para uma função que um programador não precisa especificar. Na maioria das linguagens de programação, as funções podem receber um ou mais argumentos. Normalmente, cada argumento deve ser especificado por completo (este é o caso na linguagem de programação C ). Linguagens posteriores (por exemplo, em C ++ ) permitem que o programador especifique argumentos padrão que sempre têm um valor, mesmo se um não for especificado ao chamar a função.

Argumentos padrão em C ++

Considere a seguinte declaração de função:

int MyFunc(int a, int b, int c = 12);

Esta função leva três argumentos, dos quais o último tem um padrão de doze. O programador pode chamar essa função de duas maneiras:

int result = MyFunc(1, 2, 3);
result = MyFunc(1, 2);

No primeiro caso, o valor do argumento denominado c é especificado como normal. No segundo caso, o argumento é omitido e o valor padrão 12 será usado em seu lugar.

Não há como saber se o argumento foi especificado pelo chamador ou se o valor padrão foi usado.

O método mencionado acima é especialmente útil quando se deseja definir critérios padrão para que a função possa ser chamada com ou sem parâmetros. Considere o seguinte:

void PrintGreeting(std::ostream& stream = std::cout) {
  // This outputs a message to the given stream.
  stream << "hello world!";
}

A chamada de função:

PrintGreeting();

irá por padrão imprimir " hello world !" para a saída padrão std::cout (normalmente a tela). Por outro lado, qualquer objeto do tipo std::ostreamagora pode ser passado para a mesma função e a função será impressa no fluxo fornecido em vez de na saída padrão.

PrintGreeting(std::cerr);

Como os valores dos argumentos padrão são "preenchidos" no site da chamada, e não no corpo da função que está sendo chamada, as funções virtuais obtêm seus valores de argumento padrão do tipo estático do ponteiro ou da referência através da qual a chamada é feita, em vez de do tipo dinâmico do objeto que fornece o corpo da função virtual.

struct Base {
  virtual std::pair<int, int> Foo(int x = 1) {
    return {x, 1};
  }
};

struct Derived : public Base {
  std::pair<int, int> Foo(int x = 2) override {
    return {x, 2};
  }
};

int main() {
  Derived d;
  Base& b = d;
  assert(d.Foo() == std::make_pair(2, 2));
  assert(b.Foo() == std::make_pair(1, 2));
}

Métodos sobrecarregados

Algumas linguagens, como Java , não possuem argumentos padrão. No entanto, o mesmo comportamento pode ser simulado usando a sobrecarga de método para criar métodos sobrecarregados com o mesmo nome, que usam diferentes números de argumentos; e as versões com menos argumentos simplesmente chamam as versões com mais argumentos, com os argumentos padrão como os argumentos ausentes:

int MyFunc(int a, int b) { return MyFunc(a, b, 12); }
int MyFunc(int a, int b, int c) { /* main implementation here */ }

No entanto, além de várias outras desvantagens , uma vez que os argumentos padrão não são modelados no sistema de tipo, o tipo de retorno de chamada (também conhecido como função de ordem superior) não pode expressar que aceita qualquer uma das sobrecargas nem simular os argumentos padrão com funções sobrecarregadas. Considerando que, em JavaScript, a definição de função não sobrecarregada pode substituir o padrão quando o valor de entrada é undefined(independentemente se foi implicitamente undefinedpor meio da ausência do argumento no site da chamada ou um undefinedvalor explicitamente passado ); que é modelado como um tipo de parâmetro de argumento opcional ?: em TypeScript . A solução do JavaScript não é resolvida estaticamente (ou seja, não em tempo de compilação, que é porque o TypeScript modela apenas a opcionalidade e não os valores padrão na assinatura de tipo da função), portanto, incorre em sobrecarga de tempo de execução adicional, embora forneça mais flexibilidade para que os retornos de chamada possam independentemente controlar seus padrões em vez de ditados centralmente pela (assinatura de tipo de retorno de chamada no) assinatura de tipo da função que fornece o retorno de chamada. A solução TypeScript pode ser simulada em Java com o Optionaltipo, exceto o análogo de um implícito undefinedpara cada argumento ausente é um explícito Optional.<Integer>absent()no site da chamada.

Avaliação

Para cada chamada de função, os valores de argumento padrão devem ser passados ​​para a função chamada.

Se um valor de argumento padrão contém efeitos colaterais, é significativo quando esses efeitos colaterais são avaliados - uma vez para o programa inteiro (em tempo de análise, tempo de compilação ou tempo de carregamento), ou uma vez por chamada de função, em tempo de chamada.

Python é uma linguagem notável que avalia expressões em argumentos padrão uma vez, no momento em que a declaração da função é avaliada. Se a avaliação por chamada de função for desejada, ela pode ser replicada fazendo com que o argumento padrão seja um valor sentinela , como Nonee, em seguida, fazendo com que o corpo da função avalie os efeitos colaterais do valor padrão apenas se o valor sentinela for passado.

Por exemplo:

import random

def eager(a=random.random()):
    return a

x = eager()
y = eager()
assert x == y

def lazy(a=None):
    if a is None:
        a = random.random()
    return a

x = lazy()
y = lazy()
assert x != y

Extensão

Geralmente um argumento padrão se comporta de forma idêntica a um argumento passado por parâmetro ou uma variável local declarada no início da função, e tem o mesmo escopo e extensão (tempo de vida) como um parâmetro ou outra variável local, ou seja, uma variável automática que é desalocada no encerramento da função.

Em outros casos, um argumento padrão pode ser alocado estaticamente. Se a variável for mutável, ela reterá seu valor nas chamadas de função, como acontece com uma variável estática .

Esse comportamento é encontrado em Python para tipos mutáveis, como listas. Tal como acontece com a avaliação, para garantir a mesma extensão que uma variável local, pode-se usar um valor sentinela:

def eager(a=[]):
    return a

x = eager()
x += [1]
assert eager() == [1]

def lazy(a=None):
    if a is None:
        a = []
    return a

x = lazy()
x += [1]
assert lazy() == []

Referências