- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我有一组用作模板参数的类。它们都符合一些非正式的接口(interface)(又名概念)
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 很棒,但它需要每个结构都进行 self 修改才能满足您对拥有 c 的要求。实际上,c 的代码是您的问题,而不是您输入的数据的问题。
当然,您会希望零开销,CRTP 和这种方法都能实现。
因此,我们有条件地调用 .c()
或根据它的存在调用 .a()+.b()
。这里有两种方法:
创建一个自由函数 c
:
template<class T, class...Ignored>
decltype(auto) c(T& t, Ignored&&...)
它分派(dispatch)到两个实现:
{
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();
}
}
我们很好。另请注意,如果 c(t)
的命名空间中有一个免费的非可变参数函数 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 )
在类型(包括 template
的 r/l 值限定)上计算 arg
。如果结果类型的实例在 constexpr 上下文中返回 true,则运行 if_true
。如果它在 constexpr 竞赛中返回 false,则运行 if_false
。
在这两种情况下,arg
都会传递给选定的 lambda。
连同 C++14 的 auto
lambda 支持,这让您可以编写相对简洁的条件编译代码。
未运行的 lambda 只是一个未实例化的模板。 run lambda 是用 arg 实例实例化的。因此,未运行的 lambda 在未被选中的情况下不需要包含有效代码。
branch
的类型实际上是在两个选项之间静态选择的;他们实际上可以返回不同的类型。没有完成任何转换。
branch
的 if_false-less 重载返回 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...>;
有人提议将非常像 can_apply
的东西添加到 std
。
请注意我上面对 decltype(x)(x)
的非惯用用法。这相当于在 std::forward<X>(x)
是转发引用的上下文中的 X
,并且也在 auto&&
参数 lambda 中工作。这意味着“将 x
转换为它声明的类型”。请注意,如果 x
是一个值(非引用)类型,它将复制它(这是更喜欢 forward 的原因,它永远不会这样做):然而,在任何情况下都不是这种情况我上面的 decltype(x)(x)
使用。
关于C++模板参数默认函数实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34797182/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!