27

(I'll restrict this question to C++11, since I believe there is no general way to do this in C++98).

Supposed I have a complicated (in terms of signature) set of template functions and/or overloaded functions, and I want to use these functions in the exact same way but using a different name (that is, an alias).

For example:

template<class A, class B, class C> 
D fun(A a, B& b, C&& c){ ... }

template<class E, class F> 
G fun(H<E> he, F& f){ ... }

... many other versions of fun

Now suppose that I want to rename (or alias, or forward to be more precise) these functions, all at once (i.e. be able to use a different name for the same function without rewriting it). Such that, in other parts of the code I can use it with a different name and without modifying the above code.

Is this the correct way to rename (alias/forward) fun into gun?

template<typename... Args> 
inline auto gun(Args&&... args)->decltype(fun(std::forward<Args>(args)...)){
    return fun(std::forward<Args>(args)...);
}
  • Is it truly general?
  • Is this the simplest way?
  • Is this the optimal way? (e.g. can be inlined, no unnecessary copies)
  • What if the original function had some SFINAE features? (e.g. template<class A, class B, class C, class = std::enable_if< ... >::type>), will decltype transfer the SFINAE in all cases?
  • What if the original function returned references? Isn't the decltype going to remove the references type?. (e.g. double& fun(double& x){return x;}).
  • Can same be said about member functions?

Clarification: gun is never going to be exactly fun, as the instances will have different addresses, but what I am looking for is a rename for the point of view of generic coding.

Comment: I find strange that almost everything can be renamed/forwarded, namespaces, types (typedef) and template types using typedef, but not functions (or for that matter member functions).


EDIT: For completeness, and since this seems to be the way to do it, here I added a macro to define function aliases:

#define ALIAS_FUNCTION(OriginalnamE, AliasnamE) \
template <typename... Args> \
inline auto AliasnamE(Args&&... args) -> decltype(OriginalnamE(std::forward<Args>(args)...)) { \
  return OriginalnamE(std::forward<Args>(args)...); \
}

and then you use it like this:

namespace NS1{namepsace NS2{
  ALIAS_FUNCTION(NSA::fun, gun); // second argument (target name can't have namespace)
}}

EDIT2: I think exception policy can be incorporated in the alias as well:

#define ALIAS_FUNCTION(OriginalnamE, AliasnamE) \
template <typename... Args> \
inline auto AliasnamE(Args&&... args) \
  noexcept(OriginalnamE(std::forward<Args>(args)...)) \
->decltype(OriginalnamE(std::forward<Args>(args)...)){\
    return OriginalnamE(std::forward<Args>(args)...);}

but didn't test it yet. You must type it three times.

alfC
  • 14,261
  • 4
  • 67
  • 118
  • 2
    I think this is fine. `decltype` gets the correct type (i.e. `&` for a function that returns an lvalue reference, `&&` for a function that returns an rvalue reference, and non-reference otherwise). – Kerrek SB Dec 04 '12 at 09:21
  • 7
    Please, you'll make life a lot easier for everyone if you describe what you're actually doing. You're not renaming *anything*. The original name still exists. You're forwarding, or providing an alias or a wrapper function. None of this has anything to do with renaming. Renaming implies *changing* the name, making the old name go away in favor of a new one – jalf Dec 04 '12 at 10:06
  • @jalf, good point. I guess, it is harder to make the original name to go away. As the only way I imagine is to enclose the original definition into an unnamed namespace. But yes, that was not the idea. – alfC Dec 04 '12 at 10:24
  • From my understanding, it should... but *everytime* and the C++ standard don't mesh well enough for me to provide any guarantee :x – Matthieu M. Dec 04 '12 at 10:59
  • 1
    The implementation itself looks fine to me, but you should also declare it `inline`. Template functions aren't implicitly declared `inline` unless they're defined inside a class, so if you want the compiler to inline the functions generated from `gun`, you have to say so. (Compilers are permitted to inline functions not declared `inline`, but if you want to maximize the likelihood that `gun` will be inlined, explicitly using `inline` seems prudent.) – KnowItAllWannabe Dec 04 '12 at 11:22
  • I think `#define gun fun` is okay. It works everywhere. – zch Dec 04 '12 at 12:37
  • @zch, the problem I see with `#define` is that it will add the name `gun` even in contexts where `fun` is not used as function, isn't it? – alfC Dec 04 '12 at 16:12
  • @KnowItAllWannabe, I added the `inline` to make the code more complete in the question. – alfC Dec 04 '12 at 16:14
  • @alfC: I agree. I did suggest "alias" in my comment too. :) – jalf Dec 04 '12 at 20:50
  • Since you are clearly a beginner, I have not downvoted, but: I cannot answer your question because your code does not even compile. Please read the help section and provide an [mcve](https://stackoverflow.com/help/minimal-reproducible-example). ;-) – Peter - Reinstate Monica Mar 07 '21 at 20:15
  • @Peter-ReinstateMonica no problem, the question has been answered already, some time ago. – alfC Mar 07 '21 at 21:04

1 Answers1

14

Is it truly general?

Yes.

Is this the simplest way?

Yes (sadly).

Is this the optimal way? (e.g. can be inlined, no unnecessary copies)

Yes.

What if the original function had some SFINAE features? (e.g. template<class A, class B, class C, class = std::enable_if< ... >::type>), will decltype transfer the SFINAE in all cases?

Yes, if the expression inside decltype yields an error (i.e., fun doesn't exist, perhaps due to SFINAE on fun), this will trigger SFINAE for gun aswell, removing it from the overload set.

What if the original function returned references? Isn't the decltype going to remove the references type?. (e.g. double& fun(double& x){return x;}).

No, why would it?

Same can be said about member functions, although this has to be done modifying the class I guess.

This isn't a question. What can be said about member functions? All of the above applies equally.

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • 2
    +1 for the "sadly". Since I didn't receive an answer for the negative, I'll mark this as accepted. – alfC Dec 04 '12 at 16:15