Scr*_*dib 6 c++ templates metaprogramming
编辑:我的问题的简短回答是,我对SFINAE可以做什么有错误的看法,而且根本没有检查函数体:sfinae是否实例化了一个函数体?
我有一个与此类似的问题:是否可以编写模板来检查函数的存在?
不同之处在于我不仅想检查函数是否存在,而且我还想知道它是否真的会通过SFINAE.这是我想要完成的一个例子:
struct A
{
void FuncA() { std::cout << "A::FuncA" << std::endl; }
};
struct B
{
void FuncA() { std::cout << "B::FuncA" << std::endl; }
void FuncB() { std::cout << "B::FuncB" << std::endl; }
};
template<typename T>
struct Inter
{
void FuncA() { t.FuncA(); }
void FuncB() { t.FuncB(); }
T t;
};
// Always takes some sort of Inter<T>.
template<typename InterType>
struct Final
{
void CallFuncs()
{
// if( t.FuncA() exists and can be called )
t.FuncA();
// if( t.FuncB() exists and can be called )
t.FuncB();
}
InterType t;
};
void DoEverything()
{
Final<Inter<A>> finalA;
Final<Inter<B>> finalB;
finalA.CallFuncs();
finalB.CallFuncs();
}
Run Code Online (Sandbox Code Playgroud)
请注意,在CallFuncs()中,FuncA()和FuncB()都将始终存在,但它们可能无法编译,具体取决于Inter中使用的类型T. 当我试图在上面的链接问题中使用答案时,它似乎总是给我真实,我猜是因为它只是检查函数是否存在,而不是它实际上可以被编译(尽管我不能排除我没有搞砸了......)
为了有条件地调用我认为的函数,我可以使用enable_if:
template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
i.FuncA();
}
template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
i.FuncB();
}
template<typename InterType>
struct Final
{
void CallFuncs()
{
TryCallFuncA(t);
TryCallFuncB(t);
}
InterType t;
};
Run Code Online (Sandbox Code Playgroud)
但我不确定是否有任何方法可以将布尔值传递给enable_if.有什么方法可以实现这一点,还是我需要回到某种手动维护的类型特征来指示函数是否存在?
就可用的C++ 11功能集来说,它是值得的,我正在使用MSVC 2010.
编辑:要添加一个重要的注释,在我的实际情况中,类Inter的实现实际上是不透明的,我需要确定Inter :: FuncA/FuncB是否会编译,所以我不能只是冒泡孩子类型并检查它们上是否存在函数.
我现在没有时间检查这个,但你可以添加一个专门化Final:( template <typename T> struct Final< Inner<T> >;这也有助于确保类型始终是一个Inner.有了它,你可以提取用于实例化的类型Inter.
现在第二个问题是如何使用SFINAE来检测成员函数是否存在.我相信这不应该太复杂(如果你不需要使这个通用):
// Find out whether U has `void f()` member
template <typename U>
struct has_member_f {
typedef char yes;
struct no { char _[2]; };
template<typename T, void (T::*)() = &T::f>
static yes impl( T* );
static no impl(...);
enum { value = sizeof( impl( static_cast<U*>(0) ) ) == sizeof(yes) };
};
Run Code Online (Sandbox Code Playgroud)
您可以稍微扩展一下以使其更通用一些,但我认为您不能通用的函数名称.当然,您可以将其编写为生成has_member_##arg和使用的宏&T:: arg.成员的类型可能更容易概括......
或者,因为我不认为这可以是通用的,你可以has_member在你的类型中直接使用内部技巧:提供两个callFuncA重载,一个使用可选的第二个参数进行模板化,使用你想要的签名并默认为&T::FuncA转发调用,另一个省略号是noop.然后callFuncs会叫callFuncA和callFuncB和SFINAE就会分派到无论是转发还是中午,你会得到你想要的行为.
template<typename T>
struct Final< Inter<T> >
{
template <typename U, void (U::*)() = &U::FuncA>
void callFuncA( Inter<T>* x ) {
x.FuncA();
}
void callFuncA(...) {}
void CallFuncs() {
callFuncA(&t); // Cannot pass nonPOD types through ...
// Similarly TryCallFuncB(t);
}
Inter<T> t;
};
Run Code Online (Sandbox Code Playgroud)