C++模板参数默认函数实现
C++ template parameter default function implementation
我有一组 classes 用作模板的参数。
它们都符合一些非正式的接口(a.k.a.一个概念)
template <typename T>
int func( T& t ) { return t.a() + t.b() + t.c(); }
在这个例子中,假设我使用 Foo
或 Bar
作为参数实例化模板,因此它们必须实现方法 a
b
和 c
.
struct Foo { int a(); int b(); int c(); };
struct Bar { int a(); int b(); int c(); };
现在,我有很多这样的 classes,我想根据其他功能对其中一个功能进行默认实现。
比如我想要c
到return默认a()
和b()
的区别。所以我希望我定义 a()
和 b()
和 c()
将自动实现为 int c() { return a()- b();}
就足够了,而不必为所有 class 复制此代码es.
我曾经使用多态性(通过将 a()
和 b()
定义为基础 class 中的纯虚函数,默认(虚拟)实现 c()
), 但出于性能原因我放弃了这种机制。
我想知道是否有推荐的解决方案可以使用我的模板参数 classes.
获得这种结果(即编写一次默认实现)
如何使用 CRTP 为继承自它的 类 提供默认实现:
template <typename Child>
class DefaultC
{
public:
int c() { Child& this_obj = static_cast<Child&>(*this); return this_obj.a()- this_obj.b();}
};
然后:
struct Foo : public DefaultC<Foo> { int a(); int b(); };
(请注意,如果您的函数是 non-mutating,请将它们标记为常量)
我会先尝试 CRTP:
template < typename Derived >
struct subtract
{
int c() const
{
auto this_ = static_cast<Derived const*>(this);
return this_->a() - this_->b();
}
};
struct whatnot : subtract<whatnot>
{
int a() const { return 42; }
int b() const { return 666; }
};
我很想从 std::begin
窃取一页。
CRTP 很棒,但它需要每个结构都进行自我修改才能满足您拥有 c 的要求。实际上,c 的代码是您的问题,而不是您输入的数据的问题。
您自然会想要 zero-overhead,CRTP 和此方法都能实现。
因此,我们有条件地调用 .c()
或根据其存在调用 .a()+.b()
。这里有两种方法:
创建一个免费函数c
:
template<class T, class...Ignored>
decltype(auto) c(T& t, Ignored&&...)
它分派到两个实现:
{
auto which = has_c_method<T>;
return details::c(which{}, t);
}
其中 has_c_method
是一个 traits bool 类型,用于检测传递的类型是否具有 .c()
方法。 (我在下面写一个)。
在名称空间详细信息中:
namespace details{
template<class T>
auto c(std::false_type, T&){
return t.a()-t.b();
}
template<class T>
auto c(std::true_type, T&){
return t.c();
}
}
我们很好。另请注意,如果 t
的命名空间中有一个免费的 non-variadic 函数 c(t)
,它将是首选(这就是 Ignored
所做的)。
您确实必须写出这些特征 class,但很多 SO 答案都涵盖了这一点。
建议使用比 c
更好的名称。 ;)
这种设计的优点是不会强迫人们编写您的目标类型来参与操作。您只需访问 t.c()
或 t.a()+t.b()
,具体取决于是否定义了 t.c()
。
现在我们可以从更通用的方向来解决这个问题。我们不会创建一个 c
函数来为我们调度,而是...
我们写一个compile-time分支:
namespace details {
template<bool>
struct branch {
template<class T, class F_true, class F_false>
std::result_of_t<F_true(T)> operator()( T&&t, F_true&&f, F_false&&){
return decltype(f)(f)(decltype(t)(t));
}
};
template<>
struct branch<false> {
template<class T, class F_true, class F_false>
std::result_of_t<F_false(T)> branch( T&& t, F_true&&, F_false&&f){
return decltype(f)(f)(decltype(t)(t));
}
};
}
template<template<class...>class Z, class T, class F_true, class F_false>
auto branch( T&& t, F_true&& f_true, F_false&& f_false )
-> decltype( details::branch<Z<T>{}>{}(std::declval<T>(), std::declval<F_true>(), std::declval<F_false>() )
{
return details::branch<Z<T>{}>{}(decltype(t)(t), decltype(f_true)(f_true), decltype(f_false)(f_false) );
}
无虚假案例:
template<template<class...>class Z, class T, class F_true>
void branch( T&& t, F_true&& f_true )
{
branch( std::forward<T>(t), std::forward<F_true>(f_true), [](auto&&){} );
}
使用:
int c = branch<has_c_method>(
t,
[&](auto& t){ return t.c(); },
[&](auto& t){ return t.a()-t.b(); }
);
这可以让你做得更多 ad-hoc。
branch<template>( arg, if_true, if_false )
在 arg
类型(包括 r/l 值限定)上计算 template
。如果在 constexpr 上下文中结果 return 的类型实例为真,则 if_true
为 运行。如果在 constexpr 竞赛中 return 为假,则 if_false
为 运行。
在这两种情况下,arg
都会传递给选定的 lambda。
连同 C++14 的 auto
lambda 支持,这让您可以编写相对简洁的条件编译代码。
un运行 lambda 只是一个未实例化的模板。 运行 lambda 使用 arg 实例进行实例化。因此 un运行 lambda 在未被选中的情况下不需要包含有效代码。
branch
的类型实际上是在两个选项之间静态选择的;他们实际上可以 return 不同的类型。没有完成转换。
branch
returns void
的if_false-less重载,因为我比较懒,没觉得有多大用处。
这里是 has_c_method
用大部分通用代码编写的草图。
namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply_helper:
std::false_type
{};
template<template<class...>class Z, class...Ts>
struct can_apply_helper<Z, std::void_t<Z<Ts...>>, Ts...>:
std::true_type
{};
}
// is true_type iff Z<Ts...> is valid:
template<template<class...>class Z, class...Ts>
using can_apply = typename details::can_apply_helper<Z, void, Ts...>::type;
// return type of t.c(args...). Easy to write
// and with the above, makes has_c_method a one-liner:
template<class T, class...Args>
using c_method_result = decltype(std::declval<T>().c(std::declval<Args>()...));
template<class T, class...Args>
using has_c_method = can_apply<c_method_result, T, Args...>;
有人提议将非常类似于 can_apply
的内容添加到 std
。
注意我在上面 non-idiomatic 对 decltype(x)(x)
的使用。这相当于 std::forward<X>(x)
在 X
是转发引用的上下文中,并且在 auto&&
参数 lambda 中也有效。意思是"cast x
to the type it was declared with"。请注意,如果 x
是值 (non-reference) 类型,它将 复制 它(这是更喜欢 forward 的原因,它永远不会这样做): , 然而,在我上面的任何 decltype(x)(x)
使用中都不是这种情况。
一个版本,灵感来自 Kerrek 的评论,但使用 std::true_type
和 std::false_type
#include <iostream>
#include <type_traits>
struct Foo {
int a() { return 10; }
int b() { return 20; }
int c() { return 30; }
};
struct Bar {
int a() { return 8; }
int b() { return 3; }
};
template<typename T, typename = void>
struct has_c : std::false_type {
static int call(T t) { return t.a() - t.b(); }
};
template<typename T>
struct has_c<T, decltype(std::declval<T>().c(), void())> : std::true_type {
static int call(T t) { return t.c(); }
};
template <typename T>
int f(T&& t) {
return has_c<T>::call(std::forward<T>(t));
}
int main()
{
Foo foo;
Bar bar;
std::cout << f(foo) << "\n";
std::cout << f(bar) << "\n";
return 0;
}
我有一组 classes 用作模板的参数。 它们都符合一些非正式的接口(a.k.a.一个概念)
template <typename T>
int func( T& t ) { return t.a() + t.b() + t.c(); }
在这个例子中,假设我使用 Foo
或 Bar
作为参数实例化模板,因此它们必须实现方法 a
b
和 c
.
struct Foo { int a(); int b(); int c(); };
struct Bar { int a(); int b(); int c(); };
现在,我有很多这样的 classes,我想根据其他功能对其中一个功能进行默认实现。
比如我想要c
到return默认a()
和b()
的区别。所以我希望我定义 a()
和 b()
和 c()
将自动实现为 int c() { return a()- b();}
就足够了,而不必为所有 class 复制此代码es.
我曾经使用多态性(通过将 a()
和 b()
定义为基础 class 中的纯虚函数,默认(虚拟)实现 c()
), 但出于性能原因我放弃了这种机制。
我想知道是否有推荐的解决方案可以使用我的模板参数 classes.
获得这种结果(即编写一次默认实现)如何使用 CRTP 为继承自它的 类 提供默认实现:
template <typename Child>
class DefaultC
{
public:
int c() { Child& this_obj = static_cast<Child&>(*this); return this_obj.a()- this_obj.b();}
};
然后:
struct Foo : public DefaultC<Foo> { int a(); int b(); };
(请注意,如果您的函数是 non-mutating,请将它们标记为常量)
我会先尝试 CRTP:
template < typename Derived >
struct subtract
{
int c() const
{
auto this_ = static_cast<Derived const*>(this);
return this_->a() - this_->b();
}
};
struct whatnot : subtract<whatnot>
{
int a() const { return 42; }
int b() const { return 666; }
};
我很想从 std::begin
窃取一页。
CRTP 很棒,但它需要每个结构都进行自我修改才能满足您拥有 c 的要求。实际上,c 的代码是您的问题,而不是您输入的数据的问题。
您自然会想要 zero-overhead,CRTP 和此方法都能实现。
因此,我们有条件地调用 .c()
或根据其存在调用 .a()+.b()
。这里有两种方法:
创建一个免费函数c
:
template<class T, class...Ignored>
decltype(auto) c(T& t, Ignored&&...)
它分派到两个实现:
{
auto which = has_c_method<T>;
return details::c(which{}, t);
}
其中 has_c_method
是一个 traits bool 类型,用于检测传递的类型是否具有 .c()
方法。 (我在下面写一个)。
在名称空间详细信息中:
namespace details{
template<class T>
auto c(std::false_type, T&){
return t.a()-t.b();
}
template<class T>
auto c(std::true_type, T&){
return t.c();
}
}
我们很好。另请注意,如果 t
的命名空间中有一个免费的 non-variadic 函数 c(t)
,它将是首选(这就是 Ignored
所做的)。
您确实必须写出这些特征 class,但很多 SO 答案都涵盖了这一点。
建议使用比 c
更好的名称。 ;)
这种设计的优点是不会强迫人们编写您的目标类型来参与操作。您只需访问 t.c()
或 t.a()+t.b()
,具体取决于是否定义了 t.c()
。
现在我们可以从更通用的方向来解决这个问题。我们不会创建一个 c
函数来为我们调度,而是...
我们写一个compile-time分支:
namespace details {
template<bool>
struct branch {
template<class T, class F_true, class F_false>
std::result_of_t<F_true(T)> operator()( T&&t, F_true&&f, F_false&&){
return decltype(f)(f)(decltype(t)(t));
}
};
template<>
struct branch<false> {
template<class T, class F_true, class F_false>
std::result_of_t<F_false(T)> branch( T&& t, F_true&&, F_false&&f){
return decltype(f)(f)(decltype(t)(t));
}
};
}
template<template<class...>class Z, class T, class F_true, class F_false>
auto branch( T&& t, F_true&& f_true, F_false&& f_false )
-> decltype( details::branch<Z<T>{}>{}(std::declval<T>(), std::declval<F_true>(), std::declval<F_false>() )
{
return details::branch<Z<T>{}>{}(decltype(t)(t), decltype(f_true)(f_true), decltype(f_false)(f_false) );
}
无虚假案例:
template<template<class...>class Z, class T, class F_true>
void branch( T&& t, F_true&& f_true )
{
branch( std::forward<T>(t), std::forward<F_true>(f_true), [](auto&&){} );
}
使用:
int c = branch<has_c_method>(
t,
[&](auto& t){ return t.c(); },
[&](auto& t){ return t.a()-t.b(); }
);
这可以让你做得更多 ad-hoc。
branch<template>( arg, if_true, if_false )
在 arg
类型(包括 r/l 值限定)上计算 template
。如果在 constexpr 上下文中结果 return 的类型实例为真,则 if_true
为 运行。如果在 constexpr 竞赛中 return 为假,则 if_false
为 运行。
在这两种情况下,arg
都会传递给选定的 lambda。
连同 C++14 的 auto
lambda 支持,这让您可以编写相对简洁的条件编译代码。
un运行 lambda 只是一个未实例化的模板。 运行 lambda 使用 arg 实例进行实例化。因此 un运行 lambda 在未被选中的情况下不需要包含有效代码。
branch
的类型实际上是在两个选项之间静态选择的;他们实际上可以 return 不同的类型。没有完成转换。
branch
returns void
的if_false-less重载,因为我比较懒,没觉得有多大用处。
这里是 has_c_method
用大部分通用代码编写的草图。
namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply_helper:
std::false_type
{};
template<template<class...>class Z, class...Ts>
struct can_apply_helper<Z, std::void_t<Z<Ts...>>, Ts...>:
std::true_type
{};
}
// is true_type iff Z<Ts...> is valid:
template<template<class...>class Z, class...Ts>
using can_apply = typename details::can_apply_helper<Z, void, Ts...>::type;
// return type of t.c(args...). Easy to write
// and with the above, makes has_c_method a one-liner:
template<class T, class...Args>
using c_method_result = decltype(std::declval<T>().c(std::declval<Args>()...));
template<class T, class...Args>
using has_c_method = can_apply<c_method_result, T, Args...>;
有人提议将非常类似于 can_apply
的内容添加到 std
。
注意我在上面 non-idiomatic 对 decltype(x)(x)
的使用。这相当于 std::forward<X>(x)
在 X
是转发引用的上下文中,并且在 auto&&
参数 lambda 中也有效。意思是"cast x
to the type it was declared with"。请注意,如果 x
是值 (non-reference) 类型,它将 复制 它(这是更喜欢 forward 的原因,它永远不会这样做): , 然而,在我上面的任何 decltype(x)(x)
使用中都不是这种情况。
一个版本,灵感来自 Kerrek 的评论,但使用 std::true_type
和 std::false_type
#include <iostream>
#include <type_traits>
struct Foo {
int a() { return 10; }
int b() { return 20; }
int c() { return 30; }
};
struct Bar {
int a() { return 8; }
int b() { return 3; }
};
template<typename T, typename = void>
struct has_c : std::false_type {
static int call(T t) { return t.a() - t.b(); }
};
template<typename T>
struct has_c<T, decltype(std::declval<T>().c(), void())> : std::true_type {
static int call(T t) { return t.c(); }
};
template <typename T>
int f(T&& t) {
return has_c<T>::call(std::forward<T>(t));
}
int main()
{
Foo foo;
Bar bar;
std::cout << f(foo) << "\n";
std::cout << f(bar) << "\n";
return 0;
}