在C++中将n个二进制调用转换为一个n-ary调用?

Mar*_* Ba 7 c++ templates c++11

我们的代码库中有一个辅助函数来连接两个(Windows)路径字符串:

CString AppendPath(CString const& part1, CString const& part2);
Run Code Online (Sandbox Code Playgroud)

它通常以这种方式使用:

const CString filePath = AppendPath(AppendPath(AppendPath(base, toplevel), sub1), filename);
Run Code Online (Sandbox Code Playgroud)

这是可以接受的,但它让我想知道在C++(或C++ 0x)中是否有可能使用(模板?)函数将二进制函数调用链接在一起.

也就是说,给定一个函数T f(T arg1, T arg2)是否可以编写一个函数T ncall(FnT fn, T arg1, T arg2, T arg3, ...),f就像我上面的例子一样调用并返回结果?

// could roughly look like this with my example:
const CString filePath = ncall(&AppendPath, base, toplevel, sub1, filename);
Run Code Online (Sandbox Code Playgroud)

请问,这个问题是关于转换而不是关于处理或连接路径字符串的最佳方法!


编辑:感谢deft_code答案为我所要求的提供了正确的术语:折叠(高阶函数).(请注意,我已经决定接受Matthieu的答案,因为他的解决方案不需要C++ 0x.)

Mat*_* M. 13

没有C++ 0x,也可以使用链接(我不建议重载逗号运算符,语法变得怪异).

语法有些不同,但非常接近:

CString const Path = AppendPath(base)(toplevel)(sub1)(filename);
Run Code Online (Sandbox Code Playgroud)

这只需通过创建一个临时对象来完成,该临时对象将通过重载执行连接,operator()并且可以通过隐式转换operator CString() const.

class AppenderPath
{
public:
  AppenderPath(){}
  AppenderPath(CString s): _stream(s) {}

  AppenderPath& operator()(CString const& rhs) {
    _stream += "/";
    _stream += rhs;
    return *this;
  }

  operator CString() const { return _stream; }

private:
  CString _stream;
};
Run Code Online (Sandbox Code Playgroud)

然后,你调整AppendPath返回这样一个对象:

AppenderPath AppendPath(CString s) { return AppenderPath(s); }
Run Code Online (Sandbox Code Playgroud)

(注意,实际上你可以直接命名AppendPath)

根据@ Martin的建议使其成为通用:

#include <iostream>
#include <string>

template <typename L, typename R>
class Fold1l
{
public:
  typedef void (*Func)(L&, R const&);

  Fold1l(Func func, L l): _func(func), _acc(l) {}

  Fold1l& operator()(R const& r) { (*_func)(_acc, r); return *this; }

  operator L() const { return _acc; }

private:
  Func _func;
  L _acc;
};

// U is just to foil argument deduction issue,
// since we only want U to be convertible into a R
template <typename R, typename L, typename U>
Fold1l<R,L> fold1l(void (*func)(L&, R const&), U l) {
  return Fold1l<R,L>(func, l);
}

void AppendPath(std::string& path, std::string const& next) {
  path += "/"; path += next;
}

int main() {
  std::string const path = fold1l(AppendPath, "base")("next");
  std::cout << path << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

代码在ideone上验证.


R. *_*des 10

在C++ 0x中,您可以使用可变参数模板.也许是这样的事情:

template<typename... Args>
CString AppendAllPaths(CString const& part1, Args const&... partn)
{
    return AppendPath(part1, AppendAllPaths(partn...));
}

template<>
CString AppendAllPaths(CString const& part1, CString const& part2)
{
    return AppendPath(part1, part2);
}
Run Code Online (Sandbox Code Playgroud)


Fre*_*urk 6

Martinho Fernandes的解决方案更通用:

#define AUTO_RETURN(EXPR) -> decltype(EXPR) \
{ return EXPR; }

template<class F, class Arg1, class ...Args>
auto n_binary_to_1_nary(F func, Arg1 &&a, Args &&...rest)
AUTO_RETURN(func(std::forward<Arg1>(a),
                 n_binary_to_1_nary(func, std::forward<Args>(rest)...))))

template<class F, class Arg1, class Arg2>
auto n_binary_to_1_nary(F func, Arg1 &&a, Arg2 &&b)
AUTO_RETURN(func(std::forward<Arg1>(a), std::forward<Arg2>(b)))
Run Code Online (Sandbox Code Playgroud)

使用:

n_binary_to_1_nary(&AppendPath, base, toplevel, sub1, filename)
Run Code Online (Sandbox Code Playgroud)

但是,AppendPath可以简单地用这种风格编写:

CString AppendPath(CString const &part1, CString const &part2);  // existing

template<class ...Args>
CString AppendPath(CString const &a, CString const &b, Args const &...rest) {
  return AppendPath(AppendPath(a, b), rest...);
}
Run Code Online (Sandbox Code Playgroud)

当然,您可以添加此重载并在代码中透明地使用它.


或者传递initializer_list:

CString filePath = AppendPath({base, toplevel, sub1, filename});
Run Code Online (Sandbox Code Playgroud)

码:

template<class Iter>
CString AppendPath(Iter begin, Iter end) {
  CString result;
  if (begin == end) {
    assert(!"reporting an error (however you like) on an empty list of paths"
            " is probably a good idea");
  }
  else {
    result = *begin;
    while (++begin != end) {
      result = AppendPath(result, *begin);
    }
  }
  return result;
}

template<class C>
CString AppendPath(C const &c) {
  return AppendPath(c.begin(), c.end());
}
Run Code Online (Sandbox Code Playgroud)

请注意,最后一个AppendPath适用于任何类似STL的容器.您还可以将这些重载添加到代码中并透明地使用它们.