Como atribuo um alias a um nome de função em C ++?

100

É fácil criar um novo nome para um tipo, variável ou namespace. Mas como atribuo um novo nome a uma função? Por exemplo, quero usar o nome hollerpara printf. #define é óbvio ... alguma outra forma?

Soluções:

  1. #define holler printf
  2. void (*p)() = fn; //function pointer
  3. void (&r)() = fn; //function reference
  4. inline void g(){ f(); }
Agnel Kurian
fonte
Obrigado a todos vocês. Meus colegas vão adorar ver void (&NewName)(some_vector&, float, float, float, float) = OldName;no meu próximo check-in.
Agnel Kurian
19
não tanto quanto eles vão adorar ver você usar nomes aleatórios para funções de biblioteca padrão.
jalf de
2
Eu não estou mexendo printfaqui. Isso foi apenas um exemplo. O problema aqui tem mais a ver com as limitações do inglês do que com qualquer outra coisa. Tenho uma única função que atende ao propósito A e ao propósito B, mas simplesmente não consigo encontrar um único nome que atenda a ambos os propósitos aqui.
Agnel Kurian
2
@Neil, precisamente. T &a = b;cria um novo nome para b. typedefpara tipos e namespace A=B;para namespaces.
Agnel Kurian
2
Existe using BaseClass::BaseClassMethod, existe using AliasType = Type;e existe até namespace AliasNamespace = Namespace;. O que está faltando éusing AliasFunction = Function;
anton_rh

Respostas:

114

Existem diferentes abordagens:

  • Com C ++ 11 com funções não sobrecarregadas de template, você pode simplesmente usar:

    const auto& new_fn_name = old_fn_name;
  • Se esta função tiver várias sobrecargas, você deve usar static_cast:

    const auto& new_fn_name = static_cast<OVERLOADED_FN_TYPE>(old_fn_name);

    Exemplo: existem duas sobrecargas de função std::stoi

    int stoi (const string&, size_t*, int);
    int stoi (const wstring&, size_t*, int);
    

    Se você quiser fazer um alias para a primeira versão, você deve usar o seguinte:

    const auto& new_fn_name = static_cast<int(*)(const string&, size_t*, int)>(std::stoi);

    Observação: não há como criar um alias para a função sobrecarregada de forma que todas as suas versões sobrecarregadas funcionem, portanto, você deve sempre especificar qual sobrecarga de função exata deseja.

  • Com C ++ 14, você pode ir ainda mais longe com constexprvariáveis ​​de modelo. Isso permite que você crie um alias de funções com modelo:

    template<typename T>
    constexpr void old_function(/* args */);
    
    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
    
  • Além disso, começando com C ++ 11, você tem uma função chamada std::mem_fnque permite alias a funções-membro. Veja o seguinte exemplo:

    struct A {
       void f(int i) {
          std::cout << "Argument: " << i << '\n';
       }
    };
    
    
    A a;
    
    auto greet = std::mem_fn(&A::f); // alias to member function
    // prints "Argument: 5"
    greet(a, 5); // you should provide an object each time you use this alias
    
    // if you want to bind an object permanently use `std::bind`
    greet_a = std::bind(greet, a, std::placeholders::_1);
    greet_a(3); // equivalent to greet(a, 3) => a.f(3);
    
Sasha.sochka
fonte
1
Excelente, que tal C ++ 98? Eu tenho uma classe com 2 sobrecargas de "redefinição" usadas para definir e redefinir. Internamente, sem problemas. Para usuários externos, eu queria definir o apelido como "definir" para que seja intuitivo para o contexto (definir um padrão construído, clear () 'd etc .; redefinir o objeto de trabalho). Métodos de classe: (1) "void (& set) (const string &, const bool, const bool);" (2) void (& set) (const string &, const int, const bool); 2 "reset" com as assinaturas correspondentes fazem o trabalho. Já que tenho a assinatura na declaração da classe, posso apenas inicializar a classe: set (reset), set (reset). Se não, seu exemplo explícito de static_cast funcionará?
Luv2code
8
Parece haver um problema com a constexprabordagem das variáveis ​​de modelo: o alias não pode fazer dedução de tipo. O compilador requer que eu forneça uma lista de parâmetros de modelo (estou escrevendo uma função de modelo variadic): não posso referir-se ao modelo de variável `alias_to_old 'sem uma lista de argumentos de modelo
usuário69818
1
constexpr auto new_fn_name = old_fn_namefunciona em C ++ 11 (pelo menos no gcc 4.9.2) e é melhor do que colocar &. Não requer que a chamada seja sempre feita por meio de um ponteiro e, portanto, permite que a função seja embutida no lugar da chamada.
ony
Com lambdas genéricos do C ++ 14, fui capaz de fazer o seguinte, o que também deve funcionar quando a função de destino tem várias sobrecargas: constexpr auto holler = [] ( auto &&...args ) { return printf( std::forward<decltype(args)>( args )... ); };
Anthony Hall
1
Usar nãostd::mem_fn é um apelido, pois faz muito mais mágica por trás do sentido.
cgsdfc de
35

Você pode criar um ponteiro de função ou uma referência de função:

void fn()
{
}

//...

void (*p)() = fn;//function pointer
void (&r)() = fn;//function reference
Brian R. Bondy
fonte
2
Isso leva o bolo. Eu não sabia sobre referências de função.
Agnel Kurian
@Vulcan: Eles são quase iguais no sentido de que você pode chamá-los com a mesma sintaxe, mas seus endereços são um pouco diferentes. r não ocupa seu próprio espaço de memória mantendo um endereço.
Brian R. Bondy
1
Como você ligaria fn, usando o alias? Você pode explicar o ponteiro de função e a referência de função? Como eles são diferentes? Eles são iguais aqui?
ma11hew28 de
1
@Matt, você o chama exatamente como chamaria de fn. r();
Agnel Kurian
como você faria isso para um método de instância? EDITAR: parece compilar:void (&r)() = this->fn;
Sam
21
typedef int (*printf_alias)(const char*, ...);
printf_alias holler = std::printf;

Você deve estar bem.

jer
fonte
Não está printf no namespace global?
Agnel Kurian
3
é global se você incluiu <stdio.h>, mas em std se você incluiu <cstdio>
Injektilo
@einpoklum: Não há nada de errado com decltype , mas a resposta é de 2010. Naquela época, não existia, decltypecomo foi introduzido em c ++ 11. Além disso, isso também deve funcionar com o bom e velho C.
Phidelux
10

int (*holler)(const char*, ...) = std::printf;

MSalters
fonte
7

Use um invólucro embutido. Você obtém ambas as APIs, mas mantém a implementação única.

John
fonte
3

De fluentcpp : ALIAS_TEMPLATE_FUNCTION (f, g)

#define ALIAS_TEMPLATE_FUNCTION(highLevelF, lowLevelF) \
template<typename... Args> \
inline auto highLevelF(Args&&... args) -> decltype(lowLevelF(std::forward<Args>(args)...)) \
{ \
    return lowLevelF(std::forward<Args>(args)...); \
}
sailfish009
fonte
1

Com lambdas genéricos do C ++ 14, consegui fazer o seguinte, o que também deve funcionar quando a função de destino tem várias sobrecargas:

constexpr auto holler = [] ( auto &&...args ) {
        return printf( std::forward<decltype(args)>( args )... );
    };
Anthony Hall
fonte
Hah! Isso me deixa triste, mesmo @ user5534993, que originalmente o pressionou a enviar esta resposta, não conseguiu fazer um voto positivo em sua direção. Bem, aqui, tem um comigo.
FeRD
0

Vale a pena mencionar aqui, IMO, que embora a pergunta original (e ótimas respostas) seja definitivamente útil se você quiser renomear uma função (há boas razões para fazer isso!), Se tudo o que você deseja fazer é retirar um namespace profundo, mas mantenha o nome, há uma usingpalavra - chave para isso:

namespace deep {
  namespace naming {
    namespace convention {
      void myFunction(int a, char b) {}
    }
  }
}
int main(void){
  // A pain to write it all out every time
  deep::naming::convention::myFunction(5, 'c');

  // Using keyword can be done this way
  using deep::naming::convention::myFunction;
  myFunction(5, 'c');  // Same as above
}

Isso também tem a vantagem de ser confinado a um escopo, embora você sempre possa usá-lo no nível superior de um arquivo. Costumo usar isso coute, endlportanto, não preciso trazer TODOS stdcom o clássico using namespace std;no início de um arquivo, mas também é útil se você estiver usando algo std::this_thread::sleep_for()em um arquivo ou função, mas não em todos os lugares, e nenhuma outra função do namespace. Como sempre, não é recomendável usá-lo em arquivos .h, ou você poluirá o namespace global.

Isso não é o mesmo que "renomear" acima, mas geralmente é o que realmente se deseja.

Kevin Anderson
fonte