考虑以下类:
class MyClass
{
public:
template<class T> typename T::result_type apply(T& func)
{
if (is_int())
{
return func(int(0));
}
return func(double(0));
}
...
};
(代码看起来不是很有用,但它只是一个人为的例子来证明我的观点)
无论如何,一个典型的仿函数应该是这样的:
struct MyFunc
{
typedef void result_type;
template<class V> void operator()(V)
{
// do something
}
};
人们会这样使用它:
MyClass c;
MyFunc f;
c.apply(f);
我的问题是 - MyClass::apply
是否可以更改以识别一个略微不同的仿函数版本除了原始版本,例如期望的版本要与所有其他参数一起传递的调用者对象引用,如下所示:
struct MyFuncEx
{
typedef void result_type;
template<class V> void operator()(const MyClass& caller, V)
{
// do something
}
};
所以,下面的代码也可以编译:
MyClass c;
MyFunc f;
c.apply(f);
MyFuncEx f2;
c.apply(f2);
作为奖励,如果仿函数包含两个重载,我希望编译失败,即以下应该编译失败:
struct MyFuncSmartAss
{
typedef void result_type;
template<class V> void operator()(V)
{
// do something
}
template<class V> void operator()(const MyClass& caller, V)
{
// do something
}
};
...
MyClass c;
c.apply(MyFuncSmartAss());
但是,只要较长的过载优先于较短的过载,这并不重要。
最佳答案
这真的取决于你是否拥有 C++11。这应该解决 C++11 中的重载问题 (*):
class MyClass {
private:
int _int;
double _double;
public:
template <typename F>
auto apply(F& f) -> decltype(f(_int), f(_double)) {
if (is_int()) { return f(_int); }
return f(_double);
}
template <typename F>
auto apply(F& f) -> decltype(f(*this, _int), f(*this, _double)) {
if (is_int()) { return f(*this, _double); }
return f(_double)
}
};
它是如何工作的?
- 删除不合适的重载:带有
decltype
的尾随返回类型规范创建了一个未评估的上下文。编译器执行表达式的常规重载解析,但只关心类型。如果发生错误(operator()
不存在于f
中),那么我们点击 SFINAE 并丢弃此apply
重载< - 如果两者都有效则有歧义:如果两个重载都合适(因为
F
提供了两个运算符)那么调用就是有歧义的
(*) 我不太确定尾随返回类型规范的正确性,更具体地说,this
和 _arg
的使用。 Clang 3.0 和 gcc 4.5.2出错了。这可以解决,只是更冗长一点。
// first
decltype(f(0), f(0.0))
// second
decltype(f(std::declval<MyClass>(), 0), f(std::declval<MyClass>(), 0.0))
在 ideone 中使用此变通方法:
编辑:应对 int/double
要求。
关于c++ - 是否可以根据方法签名中某个参数的存在或不存在来进行模板特化?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9017139/