C++模板参数默认函数实现
C++ template parameter default function implementation
我有一组用作模板参数的类。它们都符合一些非正式界面(又名概念)
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(); };
现在,我有很多这样的类,我希望有一个函数的默认实现,而不是其他函数。
例如,我希望c
默认返回a()
和b()
之间的差异。所以我希望我定义a()
和b()
就足够了c()
它会自动实现为int c() { return a()- b();}
,而无需为所有类复制此代码。
我曾经通过多态性来实现这个结果(通过将a()
和b()
定义为基类中的纯虚函数,默认(虚拟)实现为 c()
),但由于性能原因,我放弃了这种机制。
我想知道是否有推荐的解决方案来使用我的模板参数类获得这种结果(即编写一次默认实现)。
想从std::begin
那里偷一页。
CRTP 很棒,但它需要每个结构修改自身来处理您对 c 的要求。 实际上,c的代码是你的问题,而不是你被输入的数据的问题。
当然,您需要零开销,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
的命名空间中c(t)
有一个免费的非可变参数函数,它将是首选的(这就是Ignored
的作用)。
你必须写那个特征类,但许多SO答案都涵盖了这一点。
建议使用比c
更好的名称。 ;)
这种设计的优点是不会强迫编写目标类型的人参与操作。 您只需访问t.c()
或t.a()+t.b()
,具体取决于是否定义了t.c()
。
现在我们可以从更通用的方向来处理这个问题。 我们不会创建一个为我们调度的c
函数,而是......
我们编写一个编译时分支:
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(); }
);
这使您可以更加临时地执行此操作。
branch<template>( arg, if_true, if_false )
对arg
的类型(包括 r/l 值限定)template
进行评估。 如果 result 类型的实例在 constexpr 上下文中返回 true,则运行if_true
。 如果它在 constexpr 竞赛中返回 false,则运行if_false
。
在这两种情况下,arg
都会传递给选定的 lambda。
结合 C++14 auto
的 lambda 支持,这使您可以编写相对简洁地有条件编译的代码。
未运行的 lambda 只是一个未实例化的模板。 运行 lambda 使用 arg 实例化。 因此,未运行的 lambda 不需要包含未选择它的情况的有效代码。
branch
的类型实际上是在两个选项之间静态选择的;它们实际上可以返回不同的类型。 不进行转换。
branch
的if_false重载返回void
,因为我很懒,我看不出有什么用处。
下面是用大部分通用代码编写的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...>;
有人提议在std
中添加一些非常像can_apply
的东西。
请注意我对上面decltype(x)(x)
的非惯用法。 这等效于在上下文中std::forward<X>(x)
,其中 X
是转发引用,并且也适用于auto&&
参数 lambda。 它的意思是"将x
转换为声明的类型"。 请注意,如果x
是值(非引用)类型,它将复制它(这是首选 forward 的原因,它从不这样做):但是,在我的上述任何decltype(x)(x)
用途中都不是这种情况。
使用 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(); };
(请注意,如果您的函数不变异,请将它们标记为 const)
我会首先尝试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; }
};
一个版本,灵感来自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;
}
住在科里鲁
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 分段 排序函数实现中的错误
- 无法去函数实现 vim
- C++ 20 中的运算符 == 和 <=> 应该作为成员还是自由函数实现?
- 为什么在这种情况下不调用我的虚拟函数实现?
- 我能否通过将函数实现为类对象方法来避免使用互斥锁
- 嵌套的模板结构构造函数实现
- C++接口的工厂函数实现
- 链表中的递归长度函数实现
- 我可以期望某些 STL 函数实现是可自动矢量化的吗?
- 如何将深拷贝构造函数实现到链表中?
- 虚拟 CTOR 的克隆函数实现是否有问题
- 没有捕获列表的 lambda 通常作为普通函数实现吗?
- C++二叉树打印函数实现
- C++:默认构造函数实现
- C++派生类中的纯虚函数实现
- 决定放置函数实现的位置
- 强制实施纯虚函数实现,可能使用不同的参数类型
- 如何让成员函数实现依赖于类的模板参数?
- 我们如何将Ostream函数作为类的成员函数实现,而不是作为朋友函数,以便我可以用作虚拟函数