通过 sfinae 检测 API 版本

detect API version through sfinae

我在 C++ API 即将更改的代码库上工作(主分支和功能分支),我想保护我的代码免受更改,以便我的代码针对两个版本构建。作为最小工作示例 godbolt/compiler-explorer link:

常见:

class SmallWidget {
 private:
  int m_i;

 public:
  int i() { return m_i; }
};

师傅:

class BigWidget {
 private:
  std::vector<SmallWidget*> m_v;

 public:
  auto& widgets() { return m_v; }
};

功能分支:

class BigWidget {
 private:
  std::vector<SmallWidget> m_v;

 public:
  auto& widgets() { return m_v; }
};
/**/

大师版我有一些调用代码:

int single_op(BigWidget& w) {
  return (*w.widgets().begin())->i(); // asterisk will need to go
}


std::vector<int> do_stuff(std::vector<BigWidget> inputs) {
  std::vector<int> retval;
  for (auto w : inputs) {
    retval.push_back(single_op(w));
  }

  return retval;
}

API 没有预处理器变量 select 和

#if WIDGET_VERSION == OLD
  return (*w.widgets().begin())->i(); // asterisk will need to go
#else
  return (w.widgets().begin())->i(); // asterisk gone
#endif

我试图用 sfinae 检测 return 类型的 widgets

// version for the master branch API
template <typename = typename std::enable_if<std::is_same<
              std::remove_const<std::remove_reference<decltype(**(
                  std::declval<BigWidget>().widgets().begin()))>::type>::type,
              SmallWidget>::value>::type>
int single_op(BigWidget& w) {
  return (*w.widgets().begin())->i();
}

// version for the feature branch API
template <typename = typename std::enable_if<std::is_same<
              std::remove_const<std::remove_reference<decltype(*(
                  std::declval<BigWidget>().widgets().begin()))>::type>::type,
              SmallWidget>::value>::type>
int single_op(BigWidget& w) { return w.widgets().begin()->i(); }

但是编译器对此并不满意。铿锵声说:

<source>:43:46: error: failed requirement 'std::is_same<std::remove_const<std::remove_reference<decltype(* (std::declval<BigWidget>().widgets().begin()))>::type>::type, SmallWidget>::value'; 'enable_if' cannot be used to disable this declaration

模板

有没有办法在此处使用 sfinae 启用正确的代码?

我也尝试对 return 代码或函数参数使用 std::enable_if,但我的理解是编译器总是看到两个版本的签名 int single_op(BigWidget&)

我可以想出 this 版本:

// version for the feature branch API
template <typename bigtype>
int single_op(
     typename std::enable_if<
         std::is_same<
             std::remove_const<std::remove_reference<decltype(
                *(std::declval<BigWidget>().widgets().begin()))>::type>::type,
            SmallWidget>::value,
        bigtype>::type& w) {
  return w.widgets().begin()->i();
}

我为 BigWidget 类型添加了一个额外的模板参数(尽管已知)并将其用于 single_op 的函数参数。现在编译器可以遍历两个模板定义,为重载集提供一种可能的解决方案并禁用未使用的代码。

作为缺点,我现在必须在调用端拼出模板参数

retval.push_back(single_op<decltype(w)>(w));

但我怀疑有更好的解决方案。

创建一个小的辅助函数,它将接受任何一种类型和 return 您想要的类型。

SmallWidget &getWidget(SmallWidget *w) { return *w; }
SmallWidget &getWidget(SmallWidget &w) { return w; }

int single_op(BigWidget &w) {
    return getWidget(*w.widgets().begin()).i();
}

您还可以为 const 版本提供重载。