是否可以将模板专用化建立在方法签名中是否存在某个参数的基础上

Is it possible to base the template specialization on the presence or absence of a certain argument in the method signature?

本文关键字:是否 存在 基础上 参数 建立 专用 方法      更新时间:2023-10-16

考虑以下类:

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的要求