C++11: How to Alias a Function

C++11: How to alias a function?

You can define a function alias (with some work) using perfect forwarding:

template <typename... Args>
auto g(Args&&... args) -> decltype(f(std::forward<Args>(args)...)) {
return f(std::forward<Args>(args)...);
}

This solution does apply even if f is overloaded and/or a function template.

How do I assign an alias to a function name in C++?

There are different approaches:

  • With C++11 with non-template non-overloaded functions you can simply use:

    const auto& new_fn_name = old_fn_name;
  • If this function has multiple overloads you should use static_cast:

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

    Example: there are two overloads of function std::stoi

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

    If you want to make an alias to the first version you should use the following:

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

    Note: there is no way to make an alias to overloaded function such that all its overloaded versions work, so you should always specify which exact function overload you want.

  • With C++14 you can go even further with constexpr template variables. That allows you to alias templated functions:

    template<typename T>
    constexpr void old_function(/* args */);

    template<typename T>
    constexpr auto alias_to_old = old_function<T>;
  • Moreover, starting with C++11 you have a function called std::mem_fn that allows to alias member functions. See the following example:

    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);

C++ 11 conditional template alias to function

In C++11 it's not really possible to create aliases of specializations. You must create actual specializations:

template<typename T = char>
void Loc_snprintf()
{
functionA();
}

template<>
void Loc_snprintf<wchar_t>()
{
functionB();
}

With C++14 it would be possible to use variable templates to create a kind of alias.

How do I alias a member function in class space?

I would go with variadic template with perfect forwarding

class Log
{
public:
Log(std::string str)
: log(str)
{}

void print() const
{
puts(log.c_str());
}

template<typename... Ts>
auto output(Ts&&... ts) const -> decltype(print(std::forward<Ts>(ts)...))
{
return print(std::forward<Ts>(ts)...);
}

private:
std::string log;
};

If signature of print changes, there is no need to change anything in output (apart from constness, that has to be changed accordingly). The only issue is verboseness of output signature and duplication of call to print in trailing return type (which is unnecessary in C++14). The good thing is that it works even if another overload of print is added! Another issue would be in IDE, which wouldn't forward documentation comments.

Another option would be to introduce member variables referencing the function.

Creating a function alias

Here's what I do if I want to create a simple function alias

constexpr auto &&now = std::chrono::high_resolution_clock::now;

and if I want to create a full wrapper alias that will be inlined

template<typename ... Args>
inline constexpr auto now(Args &&... args) -> decltype(std::chrono::high_resolution_clock::now(std::forward<Args>(args)...)){
return std::chrono::high_resolution_clock::now(std::forward<Args>(args)...);
}

The reason why I use a universal reference auto&& in the alias definition is because of the possibility of addressof(now) == addressof(std::chrono::high_resolution_clock::now).

On my system with G++ 4.9.2 running this:

constexpr auto &&now_ref = std::chrono::high_resolution_clock::now;
constexpr auto now_var = std::chrono::high_resolution_clock::now;

template<typename ... Args>
inline constexpr auto now_wrapper(Args &&... args)
-> decltype(std::chrono::high_resolution_clock::now(std::forward<Args>(args)...)){
return std::chrono::high_resolution_clock::now(std::forward<Args>(args)...);
}

int main(int argc, char *argv[]){
std::cout << std::hex << std::showbase;
std::cout << (uintptr_t)std::addressof(std::chrono::high_resolution_clock::now) << '\n';
std::cout << (uintptr_t)std::addressof(now_wrapper<>) << '\n';
std::cout << (uintptr_t)std::addressof(now_var) << '\n';
std::cout << (uintptr_t)std::addressof(now_ref) << '\n';
}

I get the following results:

0x4007c0
0x400a50
0x400ae8
0x4007c0

Showing that only the auto&& is actually a direct alias of the function, whereas all other methods have some level of indirection. (although, after compilation they may be replaced by inlined function calls. maybe.)

Type alias for function arguments in C++

In C++14, you can just let the type be deduced:

template <typename P1, typename... Ps>
constexpr auto pipe(P1 &&proc1, Ps &&...procs) {
using T = typename RevProc<P1>::ArgType;
using U = typename RevProc<P1>::RetType;
using V = typename RevProc<typename Last<Ps...>::Type>::RetType;
return Cont<Cont<Proc<T, V>>>([&](Cont<Proc<T, V>> &&pass) {
pipe(std::move(procs)...)([&](Proc<U, V> &&proc2) {
pipe(std::move(proc1), std::move(proc2))(std::move(pass));
});
});
}

By the way, some of your uses of std::move doesn't look legitimate, since pipe may well be called with lvalues that the caller doesn't expect to be moved from. It's better to actually forward what you take by forwarding reference:

  return Cont<Cont<Proc<T, V>>>([&](Cont<Proc<T, V>> &&pass) {
pipe(std::forward<Ps>(procs)...)([&](Proc<U, V> &&proc2) {
pipe(std::forward<P1>(proc1), std::move(proc2))(std::move(pass));
});
});

Alternative to `#define` macro for function alias in C++

You can define the same function with a shorter name (in your own code). This function is named FMT() and it passes its parameters to fmt::format(), which is the original function you want to call:

template <typename S, typename... Args, typename Char = fmt::char_t<S>>
inline std::basic_string<Char> FMT(const S& format_str, Args&&... args) {
return fmt::format(format_str, std::forward<Args>(args)...);
}

Demo

C++11 template function aliases vs wrappers

You cannot alias functions, you can write new functions that forward the actual work to existing functions, but you cannot just provide aliases.

Now, it is unclear what the real question is. For starters, I would not write wrappers around the standard library features, since that is only making your code more obscure. Wether you like std::make_shared better or worse than MakeShared or not, the former is well known to any C++ programmer, while the former is something non-obvious that needs to be looked up. (And I personally don't like function names starting with capital letters, but that is a different issue).

If the question is about the implementation, considering that you already know the return type of the wrapper, I would not use decltype, which really makes the code more obscure. But it is still important to use std::forward:

template <typename T, typename... Args>
std::shared_ptr<T> MakeShared(Args&&... args)
{
return std::make_shared<T>(std::forward<Args>(args)...);
}

Adding the trailing return type and the decltype when the type is simpler to write and does not depend on the arguments at all is absurdly typing for no reason. The std::forward is needed to provide perfect forwarding of the template arguments. Otherwise you might end up calling the incorrect version of the constructor:

std::string f();
auto p = std::make_shared<std::string>(f());
// calls std::string(std::string &&)
auto q = MakeShared<std::string>(f()); // assume no std::forward was used
// calls std::string(std::string const &)

Without the std::forward in the implementation of MakeShared an rvalue passed as an argument to the function will be forwarded as an lvalue, causing the allocation of a new string, copying data and finally release of the old string at the end of the complete statement. With std::forward, the newly allocated std::string will move out of the argument string.



Related Topics



Leave a reply



Submit