Jes*_*der 34 c++ templates sfinae
我想做点什么
template <typename T>
void foo(const T& t) {
IF bar(t) would compile
bar(t);
ELSE
baz(t);
}
Run Code Online (Sandbox Code Playgroud)
我认为使用的东西enable_if
可以在这里完成工作,foo
分成两部分,但我似乎无法弄清楚细节.实现这一目标的最简单方法是什么?
Joh*_*itb 33
为名称执行了两次查找bar
.一个是在定义上下文中的非限定查找foo
.另一个是在每个实例化上下文中依赖于参数的查找(但是不允许在每个实例化上下文中查找的结果改变两个不同实例化上下文之间的行为).
要获得所需的行为,您可以在fallback
命名空间中定义回退函数,以返回一些唯一类型
namespace fallback {
// sizeof > 1
struct flag { char c[2]; };
flag bar(...);
}
Run Code Online (Sandbox Code Playgroud)
bar
如果没有其他内容匹配,则将调用该函数,因为省略号的转换成本最低.现在,通过using指令将候选者包含到您的函数中fallback
,以便将fallback::bar
其作为候选者包含在调用中bar
.
现在,要查看是否调用了bar
函数,您将调用它,并检查返回类型是否为flag
.否则选择的函数的返回类型可能是无效的,所以你必须做一些逗号操作符技巧来解决这个问题.
namespace fallback {
int operator,(flag, flag);
// map everything else to void
template<typename T>
void operator,(flag, T const&);
// sizeof 1
char operator,(int, flag);
}
Run Code Online (Sandbox Code Playgroud)
如果选择了我们的函数,则逗号运算符调用将返回对它的引用int
.如果不是,或者返回了所选函数void
,则调用将void
依次返回.然后,flag
如果选择了我们的回退,则使用第二个参数的下一次调用将返回sizeof为1的类型,并且如果选择了其他内容,则sizeof大于1(将使用内置逗号运算符,因为void
它在混合中).
我们将sizeof和delegate与结构进行比较.
template<bool>
struct foo_impl;
/* bar available */
template<>
struct foo_impl<true> {
template<typename T>
static void foo(T const &t) {
bar(t);
}
};
/* bar not available */
template<>
struct foo_impl<false> {
template<typename T>
static void foo(T const&) {
std::cout << "not available, calling baz...";
}
};
template <typename T>
void foo(const T& t) {
using namespace fallback;
foo_impl<sizeof (fallback::flag(), bar(t), fallback::flag()) != 1>
::foo(t);
}
Run Code Online (Sandbox Code Playgroud)
如果现有函数也有省略号,则此解决方案不明确.但这似乎不太可能.使用后备测试:
struct C { };
int main() {
// => "not available, calling baz..."
foo(C());
}
Run Code Online (Sandbox Code Playgroud)
并且如果找到使用参数依赖查找的候选者
struct C { };
void bar(C) {
std::cout << "called!";
}
int main() {
// => "called!"
foo(C());
}
Run Code Online (Sandbox Code Playgroud)
在定义范围内测试不合格的查找,让我们定义和以下的功能foo_impl
和foo
(把foo_impl模板上面foo
,所以它们均具有相同的定义范围内)
void bar(double d) {
std::cout << "bar(double) called!";
}
// ... foo template ...
int main() {
// => "bar(double) called!"
foo(12);
}
Run Code Online (Sandbox Code Playgroud)
litb给了你一个很好的答案.但是,我想知道,考虑到更多的背景,我们是否能够提出一些不那么通用的东西,而且还会减少,嗯,精心设计?
例如,什么类型可以T
?什么?几种类型?你可以控制的一套非常有限的套装?你设计的一些课程与功能一起foo
?鉴于后者,你可以简单地说出类似的东西
typedef boolean<true> has_bar_func;
Run Code Online (Sandbox Code Playgroud)
进入类型,然后切换到不同的foo
重载基于:
template <typename T>
void foo_impl(const T& t, boolean<true> /*has_bar_func*/);
template <typename T>
void foo_impl(const T& t, boolean<false> /*has_bar_func*/);
template <typename T>
void foo(const T& t) {
foo_impl( t, typename T::has_bar_func() );
}
Run Code Online (Sandbox Code Playgroud)
此外,bar
/ baz
函数可以有任何签名,是否有一个有限的限制集,或只有一个有效的签名?如果是后者,litb(优秀)后备理念,结合使用元功能sizeof
可能会更简单一些.但是我没有探索过,所以这只是一个想法.