在使用SFINAE编写模板专业化时,由于一个小的不存在的成员或函数,您经常需要编写一个全新的特化。我想将这个选择打包成一个小的语句,如orElse<T a,T b>
。
小例子:
template<typename T> int get(T& v){
return orElse<v.get(),0>();
}
这可能吗?
答案 0 :(得分:2)
是的,这或多或少是可能的。它被称为“成员探测器”。有关如何使用宏完成此操作,请参阅此wikibooks link。实际实现将取决于您使用的是C ++之前的版本还是之后的11,以及您正在使用的编译器。
答案 1 :(得分:2)
orElse<v.get(),0>()
的意图很清楚,但如果这样的事情可能存在,
它必须是以下之一:
调用阵容
orElse(v,&V::get,0)
orElse<V,&V::get>(v,0)
orElse<V,&V::get,0>(v)
其中v
的类型为V
,因此实例化了函数模板
将分别是:
功能模板阵容
template<typename T>
int orElse(T & obj, int(T::pmf*)(), int deflt);
template<typename T, int(T::*)()>
int orElse(T & obj, int deflt);
template<typename T, int(T::*)(), int Default>
int orElse(T & obj);
如你所知,没有这样的东西可以存在你想要的效果。
对于任何没有得到的人,
原因很简单:调用阵容中没有函数调用
如果没有V::get
这样的成员,将编译。没有回合
那个,以及被调用的函数可能是一个实例化的事实
功能模板阵容中的功能模板无论如何都没有区别。
如果V::get
不存在,则提及它的任何代码都将无法编译。
但是,您似乎有一个不需要接近的实际目标
以这种绝望的方式。对于给定名称foo
和给定类型R
,它看起来好像
您希望能够只编写一个功能模板:
template<typename T, typename ...Args>
R foo(T && obj, Args &&... args);
将返回R(T::foo)
的值,并使用参数obj
调用args...
,
如果存在这样的成员函数,否则返回一些默认的R
。
如果这是正确的,可以按照下图说明:
#include <utility>
#include <type_traits>
namespace detail {
template<typename T>
T default_ctor()
{
return T();
}
// SFINAE `R(T::get)` exists
template<typename T, typename R, R(Default)(), typename ...Args>
auto get_or_default(
T && obj,
Args &&... args) ->
std::enable_if_t<
std::is_same<R,decltype(obj.get(std::forward<Args>(args)...))
>::value,R>
{
return obj.get(std::forward<Args>(args)...);
}
// SFINAE `R(T::get)` does not exist
template<typename T, typename R, R(Default)(), typename ...Args>
R get_or_default(...)
{
return Default();
}
} //namespace detail
// This is your universal `int get(T,Args...)`
template<typename T, typename ...Args>
int get(T && obj, Args &&... args)
{
return detail::get_or_default<T&,int,detail::default_ctor>
(obj,std::forward<Args>(args)...);
}
// C++14, trivially adaptable for C++11
可以尝试:
#include <iostream>
using namespace std;
struct A
{
A(){};
int get() {
return 1;
}
int get(int i) const {
return i + i;
}
};
struct B
{
double get() {
return 2.2;
}
double get(double d) {
return d * d;
}
};
struct C{};
int main()
{
A const aconst;
A a;
B b;
C c;
cout << get(aconst) << endl; // expect 0
cout << get(a) << endl; // expect 1
cout << get(b) << endl; // expect 0
cout << get(c) << endl; // expect 0
cout << get(a,1) << endl; // expect 2
cout << get(b,2,2) << endl; // expect 0
cout << get(c,3) << endl; // expect 0
cout << get(A(),2) << endl; // expect 4
cout << get(B(),2,2) << endl; // expect 0
cout << get(C(),3) << endl; // expect 0
return 0;
}
复杂的返回类型中有“复合SFINAE”:
std::enable_if_t<
std::is_same<R,decltype(obj.get(std::forward<Args>(args)...))
>::value,R>
如果T::get
不存在,那么decltype(obj.get(std::forward<Args>(args)...)
不编译。但是如果它确实编译了,那么T::get
的返回类型就是
除R
以外的其他内容,则std::enable_if_t
类型说明符不会
编译。仅当成员函数存在且具有所需的返回类型R
时
可以R(T::get)
存在案例进行实例化。否则
catch-all R(T::get)
不存在案例被选中。
请注意get(aconst)
返回0而不是1.这是应该的,
因为无法在const A::get()
上调用非const重载A
。
您可以对任何其他R foo(V & v,Args...)
使用相同的模式
存在的或不存在的R(V::foo)(Args...)
。
如果R
不是默认构造的,或者您希望默认R
当R(V::foo)
不存在时返回与...不同的东西
R()
,然后定义返回的函数detail::fallback
(或其他)
所需的默认R
并指定它而不是detail::default_ctor
你可以进一步模板化 - 模式化这种模式真是太好了
容纳 T
的任何可能的成员函数,并提供任何可能的回报
输入R
。但是你需要的附加模板参数会
是R(T::*)(typename...)
,它的实例化值必须是
&V::get
(或其他),然后模式会
迫使你陷入致命的陷阱,提到存在存在疑问的东西。