缓存函数结果,包括 maybe void
Caching function results including maybe void
我有一个模板,其模板参数代表一个泛函。该函数在模板中调用,函数的结果应存储,如下所示:
template <class F>
class C {
F f;
/* type see below */ cached;
// ... somewhere ...
cached = f();
};
我想稍后取回存储的值。现在,即使 f
returns void
,模板也应该可以工作。由于我无法生成 void
类型的变量,所以缓存值应该是 void_placeholder_t
类型。因此我的 class 应该如下所示:
template <class F>
class C {
F f;
result_after_void_replacement_t<F()> cached;
// ... somewhere ...
cached = invoke_and_replace_void (f);
};
现在我整个下午都在研究这个问题,并提出了以下解决方案。老实说,我怀疑这个问题并不常见——所以没有标准的解决方案吗?
[对不起,冗长。例子在最后。]
#include <iostream>
#include <typeinfo>
#include <functional>
struct void_placeholder_t {};
// dummy type, since we do not want to overload comma for void_placeholder_t
struct void_replacer { };
// overload comma to return either t oder void_replacer {}
// (uses that if first argument is void, overload is not called)
template <class T>
constexpr decltype(auto) operator , (T && t, void_replacer) {
return std::forward<T> (t);
}
//
// replace_void
// helper transforming a void_replacer into a void_placeholder_t
template <class T>
constexpr decltype(auto) replace_void (T && t) {
return std::forward<T> (t);
}
constexpr void_placeholder_t replace_void (void_replacer) {
return void_placeholder_t {};
}
//
// remove_rvalue_reference
//
template<class T> struct remove_rvalue_reference { using type = T; };
template<class T> struct remove_rvalue_reference<T &&> { using type = T; };
template <class T> using remove_rvalue_reference_t
= typename remove_rvalue_reference<T>::type;
//
// result_after_void_replacement, result_after_void_replacement_t
//
template <class S> struct result_after_void_replacement;
template <class F, class ... Args>
struct result_after_void_replacement <F (Args ...)> {
using type
= remove_rvalue_reference_t < decltype (
replace_void(
( std::declval<F> () (std::declval<Args> () ...),
void_replacer {} ) )
) >;
};
template <class S>
using result_after_void_replacement_t = typename
result_after_void_replacement<S>::type;
//
// invoke_and_replace_void
//
template <class F, class ... Args>
constexpr result_after_void_replacement_t<F && (Args &&...)>
invoke_and_replace_void (F && f, Args && ... args)
{
return replace_void(
( std::forward<F> (f) (std::forward<Args> (args) ...),
void_replacer {} ) );
}
// example
void f(double) { }
double g(double d) { return d + 11.0; }
int main() {
// conversion, without invoke_and_replace_void
auto xf = replace_void ( (f(42.0), void_replacer {}) );
std::cout << typeid(xf).name () << std::endl;
auto xg = replace_void ( (g(42.0), void_replacer {}) );
std::cout << typeid(xg).name () << " " << xg << std::endl;
// conversion, with invoke_and_replace_void and no type deduction
using F = void (double);
result_after_void_replacement_t<F& (double)> zf =
invoke_and_replace_void (f, 42.0);
std::cout << typeid(zf).name () << std::endl;
using G = double (double);
result_after_void_replacement_t<G& (double)> zg =
invoke_and_replace_void (g, 42.0);
std::cout << typeid(zg).name () << " " << zg << std::endl;
return 0;
}
这应该适合你的情况
template <class F, bool>
class C_helper{
public:
F f;
/* type see below */ cached;
// ... somewhere ...
cached = f();
};
template <class F>
class C_helper<F, true> {
public:
F f;
// ... somewhere ...
f();
};
template <class F>
class C: public C_helper<F, std::is_void<std::result_of<F()>>::value> {
};
这很好用:
template <typename Fn, typename RET, typename... Args >
struct Cache
{
Cache( Fn f ) : _fn(f) {}
const RET& operator()(Args ...args)
{
_cache = _fn( std::forward<Args>( args ) ... );
return _cache;
}
Fn _fn;
RET _cache;
};
template <typename Fn, typename... Args >
struct Cache< Fn, void, Args...>
{
Cache( Fn f ) : _fn(f) {}
void operator()(Args ...args)
{
_fn( std::forward<Args>( args ) ... );
}
Fn _fn;
};
template <typename Fn, typename... Args >
using C = Cache< Fn, typename std::result_of< Fn( Args... ) >::type, Args... >;
void f(double) { }
double g(double d) { return d + 11.0; }
C<decltype(&g), double> cache_g(g);
double res = cache_g( 1.0 );
C<decltype(&f), double> cache_f(f);
cache_f( 2.0 );
没有标准解决方案,如"something which is in the C++ standard"。在 2015 年 10 月的会议上,有人试图支持 "Regular Void",但进化工作组不赞成。
通常方法是处理函数object可能return[=10=的情况] 并且该值将使用专业化进行处理。尤其是当函数模板比较复杂,公共部分不容易分解时,这种方法很烦人。
我有一个模板,其模板参数代表一个泛函。该函数在模板中调用,函数的结果应存储,如下所示:
template <class F>
class C {
F f;
/* type see below */ cached;
// ... somewhere ...
cached = f();
};
我想稍后取回存储的值。现在,即使 f
returns void
,模板也应该可以工作。由于我无法生成 void
类型的变量,所以缓存值应该是 void_placeholder_t
类型。因此我的 class 应该如下所示:
template <class F>
class C {
F f;
result_after_void_replacement_t<F()> cached;
// ... somewhere ...
cached = invoke_and_replace_void (f);
};
现在我整个下午都在研究这个问题,并提出了以下解决方案。老实说,我怀疑这个问题并不常见——所以没有标准的解决方案吗?
[对不起,冗长。例子在最后。]
#include <iostream>
#include <typeinfo>
#include <functional>
struct void_placeholder_t {};
// dummy type, since we do not want to overload comma for void_placeholder_t
struct void_replacer { };
// overload comma to return either t oder void_replacer {}
// (uses that if first argument is void, overload is not called)
template <class T>
constexpr decltype(auto) operator , (T && t, void_replacer) {
return std::forward<T> (t);
}
//
// replace_void
// helper transforming a void_replacer into a void_placeholder_t
template <class T>
constexpr decltype(auto) replace_void (T && t) {
return std::forward<T> (t);
}
constexpr void_placeholder_t replace_void (void_replacer) {
return void_placeholder_t {};
}
//
// remove_rvalue_reference
//
template<class T> struct remove_rvalue_reference { using type = T; };
template<class T> struct remove_rvalue_reference<T &&> { using type = T; };
template <class T> using remove_rvalue_reference_t
= typename remove_rvalue_reference<T>::type;
//
// result_after_void_replacement, result_after_void_replacement_t
//
template <class S> struct result_after_void_replacement;
template <class F, class ... Args>
struct result_after_void_replacement <F (Args ...)> {
using type
= remove_rvalue_reference_t < decltype (
replace_void(
( std::declval<F> () (std::declval<Args> () ...),
void_replacer {} ) )
) >;
};
template <class S>
using result_after_void_replacement_t = typename
result_after_void_replacement<S>::type;
//
// invoke_and_replace_void
//
template <class F, class ... Args>
constexpr result_after_void_replacement_t<F && (Args &&...)>
invoke_and_replace_void (F && f, Args && ... args)
{
return replace_void(
( std::forward<F> (f) (std::forward<Args> (args) ...),
void_replacer {} ) );
}
// example
void f(double) { }
double g(double d) { return d + 11.0; }
int main() {
// conversion, without invoke_and_replace_void
auto xf = replace_void ( (f(42.0), void_replacer {}) );
std::cout << typeid(xf).name () << std::endl;
auto xg = replace_void ( (g(42.0), void_replacer {}) );
std::cout << typeid(xg).name () << " " << xg << std::endl;
// conversion, with invoke_and_replace_void and no type deduction
using F = void (double);
result_after_void_replacement_t<F& (double)> zf =
invoke_and_replace_void (f, 42.0);
std::cout << typeid(zf).name () << std::endl;
using G = double (double);
result_after_void_replacement_t<G& (double)> zg =
invoke_and_replace_void (g, 42.0);
std::cout << typeid(zg).name () << " " << zg << std::endl;
return 0;
}
这应该适合你的情况
template <class F, bool>
class C_helper{
public:
F f;
/* type see below */ cached;
// ... somewhere ...
cached = f();
};
template <class F>
class C_helper<F, true> {
public:
F f;
// ... somewhere ...
f();
};
template <class F>
class C: public C_helper<F, std::is_void<std::result_of<F()>>::value> {
};
这很好用:
template <typename Fn, typename RET, typename... Args >
struct Cache
{
Cache( Fn f ) : _fn(f) {}
const RET& operator()(Args ...args)
{
_cache = _fn( std::forward<Args>( args ) ... );
return _cache;
}
Fn _fn;
RET _cache;
};
template <typename Fn, typename... Args >
struct Cache< Fn, void, Args...>
{
Cache( Fn f ) : _fn(f) {}
void operator()(Args ...args)
{
_fn( std::forward<Args>( args ) ... );
}
Fn _fn;
};
template <typename Fn, typename... Args >
using C = Cache< Fn, typename std::result_of< Fn( Args... ) >::type, Args... >;
void f(double) { }
double g(double d) { return d + 11.0; }
C<decltype(&g), double> cache_g(g);
double res = cache_g( 1.0 );
C<decltype(&f), double> cache_f(f);
cache_f( 2.0 );
没有标准解决方案,如"something which is in the C++ standard"。在 2015 年 10 月的会议上,有人试图支持 "Regular Void",但进化工作组不赞成。
通常方法是处理函数object可能return[=10=的情况] 并且该值将使用专业化进行处理。尤其是当函数模板比较复杂,公共部分不容易分解时,这种方法很烦人。