♻️

C++11 で C++20 の bind_front 使えるようにする

2020/09/26に公開

https://cpprefjp.github.io/reference/functional/bind_front.html

以下のヘッダをインクルードすれば使えるようになるはず

※ g++4.9 で確認

bind_front.hpp
#pragma once
#include <functional>
#ifndef __cpp_lib_bind_front
namespace std {

// N は bind 後の残りの引数の数
// I は再帰用 (std::placeholders::_n の数)
template <size_t N, size_t I = 0>
struct BindFront
{
	template <typename ...T>
	static auto bind(T &&...args)
		-> decltype(BindFront<N, I + 1>::bind(std::forward<T>(args)..., std::_Placeholder<I + 1>{}))
	{
		// gcc は std::_Placeholder<n>{} で std::placeholders::_n が取れる
		// clang の場合は std::placeholders::__ph<n>{} で取れる
		// VC++ は std::_Ph<n>{}
		// 再帰で std::placeholders::_n を増やしていく
		return BindFront<N, I + 1>::bind(std::forward<T>(args)..., std::_Placeholder<I + 1>{});
	}
};

// 特殊化で N == I の時 std::bind に投げる
template <size_t N>
struct BindFront<N, N>
{
	template <typename ...T>
	static auto bind(T &&...args)
		-> decltype(std::bind(std::forward<T>(args)...))
	{
		return std::bind(std::forward<T>(args)...);
	}
};

// 以下 bind_front の定義

// const なメンバ関数用
template <typename TClass, typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(TRet(TClass::*func)(TArgs...) const, TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj) + 1>::bind(func, std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj) + 1>::bind(func, std::forward<TObj>(obj)...);
}

// メンバ関数用
template <typename TClass, typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(TRet(TClass::*func)(TArgs...), TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj) + 1>::bind(func, std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj) + 1>::bind(func, std::forward<TObj>(obj)...);
}

// 関数ポインタ用
template <typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(TRet(*func)(TArgs...), TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(func, std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(func, std::forward<TObj>(obj)...);
}

// std::function 用
template <typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(const std::function<TRet(TArgs...)> &func, TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(func, std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(func, std::forward<TObj>(obj)...);
}

// std::function&& 用
template <typename TRet, typename ...TArgs, typename ...TObj>
auto bind_front(std::function<TRet(TArgs...)> &&func, TObj &&...obj)
	-> decltype(BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(std::move(func), std::forward<TObj>(obj)...))
{
	return BindFront<sizeof...(TArgs) - sizeof...(TObj)>::bind(std::move(func), std::forward<TObj>(obj)...);
}

} // std
#endif // __cpp_lib_bind_front

いちいち戻り値用の decltype に同じの書かなあかんのめんどくさい

ちなみにC++14だとこうなる

https://gist.github.com/hikarin522/60ecf4b028f353d594e06f93f52ad3f8

はやくC++17あたりになりたい

Discussion