我有需要被累加应用于单个字符串函数列表。如何表达“应用”功能。
auto outPutString = inputString
.Apply(Transformation1)
.Apply(Transformation2)
在C ++?
该字符串是的std :: string
从C ++ 11日起,您还可以使用可变参数模板编写Apply
功能:
template <typename OutputT, typename InputT>
OutputT Apply(const InputT &obj)
{
return obj;
}
template <typename OutputT, typename InputT, typename Func, typename... OtherFuncs>
OutputT Apply(const InputT &obj, Func f, OtherFuncs... other)
{
return Apply<OutputT, decltype(f(obj))>(f(obj), other...);
}
然后,你可以按如下方式使用这样的:
auto res = Apply<std::string>(
"Hello",
[](const std::string &str) { return str + " "; }, // Applicator 1
[](const std::string &str) { return str + "World"; } // Applicator 2
);
在这种情况下,结果»的Hello World«。
由于上述结构InputT
和OutputT
区别,你可以“混”类型,如:
auto res = Apply<size_t>(
"Hello",
[](const std::string &str) { return str + " World"; }, // Applicator 1
[](const std::string &str) { return str.size(); } // Applicator 2
);
这一次的结果是11
。
最后,如果你真的想用链接的语法,你可以写一个包装初始对象并具有Apply
方法的类。
像这样:
auto outPutString = Transformation2(Transformation1(inputString));
std::string manipulateString(std::string str) {/* do something */; return result;}
std::string manipulateStringAgain(std::string str) {/* do something else */; return result;}
std::string manipulateMe = "hello";
auto resultString = manipulateString(manipulateStringAgain(manipulateMe));
我会假设当你说“一个功能列表”,你所指的是在运行时发生变化。其他答案是更好,如果该列表是静态的。
#include <vector>
#include <string>
#include <functional>
#include <numeric>
std::vector<std::function<std::string(std::string)>> funcs = { Transformation1, Transformation2 }; // or gotten from wherever
auto output = std::accumulate(funcs.begin(), funcs.end(), input, [](auto acc, auto fun){ return fun(acc); });
有可能在C和C ++,以及,以限定指针功能及创建指针载体的功能。稍后,您可以用所需参数的循环中调用功能。请让我知道如果你有兴趣了解详情。
如果你想保持秩序,创造一些包装类,并把你的操作功能在里面。例如:
#include <iostream>
#include <string>
using namespace std;
class StringManipulator
{
public:
StringManipulator(std::string str) : str(str) {}
operator std::string() {return str;}
StringManipulator& doSomething() {str += "1"; return *this;}
StringManipulator& doSomethingElse() {str += "2"; return *this;}
private:
std::string str;
};
int main() {
std::string result = StringManipulator("0").doSomething().doSomethingElse();
std::cout << result;
return 0;
}
输出是012
。
operator std::string
确保隐式转换。
#include <vector>
#include <iostream>
// three funcitons with a string as the parameter
int funca(std::string& str)
{
std::cout << "funca:" << str << std::endl;
return 1;
}
int funcb(std::string& str)
{
std::cout << "funcb:" << str << std::endl;
return 2;
}
int funcd(std::string& str)
{
std::cout << "funcd:" << str << std::endl;
return 3;
}
int main()
{
// definition of the string
std::string str = "the string";
// declare vector of pointers to function returning an int and receiving a string as a parameter:
std::vector< int(*)(std::string&)> pf;
// load func pointers to vector:
pf.push_back(&funca);
pf.push_back(&funcb);
pf.push_back(&funcd);
//declare vector iterator:
std::vector<int (*)(std::string&)>::iterator it;
// iterate vector of func pointers:
for (it = pf.begin() ; it != pf.end(); ++it)
{
// function call using pointers and passing parameter str
// you can get return value as from 'normal' function
int ret = (*it)(str);
std::cout << "function returns:" << ret << std::endl;
}
}
/*
compiled and executed on ubuntu 18.04, output:
funca:the string
function returns:1
funcb:the string
function returns:2
funcd:the string
function returns:3
*/
类似的语法可以通过以下简单的包装类来定义。这使我们在写正顺序功能与链接。
f(t)
刚刚一个参数,而不是一个空洞功能f()
或多参数函数f(t1,t2,...)
。在这些情况下,发生编译错误。std::is_invocable<F,T>
和double parentheses其检查F(T)
是否形成良好与否,因为调用语法功能是通过右值引用f(std::move(t))
和其他f(t)
通之间的不同。这是我的实现:
#include <utility>
#include <type_traits>
template <typename T>
class arg
{
T t;
public:
template <typename U>
explicit constexpr arg(U&& val)
: t(std::forward<U>(val))
{}
template <typename F>
constexpr auto Apply(F f)
{
if constexpr(std::is_invocable<F, decltype((t))>::value)
{
// f(t), f(t&) and f(const t&), no type deduction
return arg<decltype(f(t))>(f(t));
}
else
{
// f(&&t), no type deduction
return arg<decltype(f(std::move(t)))>(f(std::move(t)));
}
}
constexpr auto& get() const noexcept
{
return t;
}
};
template<class U>
arg(U&&) -> arg<U&&>;
我们可以按如下方式使用该包装类。
auto outPutString = arg(inputString)
.Apply(Transformation1)
.Apply(Transformation2)
.get();
Lambda表达式是还与这种方式。下面的结果outPut
是13
:
auto outPutString = arg("Hello")
.Apply([](std::string s){ return s + " World" ; })
.Apply([](std::string& s){ return s + "!" ; })
.Apply([](const std::string& s){ return s + "!" ; })
.Apply([](std::string&& s){ return std::make_shared<std::string>(s); })
.Apply([](auto s){ return std::make_unique<std::string>(*s); })
.Apply([](auto s){ return (*s).size(); })
.get();
事实并非如此开心,但我们也可以尝试让这个类的运行时的工作如下。该输出"Hello world!"
std::string f1(const std::string& s)
{ return (s + " "); }
std::string f2(const std::string& s)
{ return (s + "World"); }
std::string f3(const std::string& s)
{ return (s + "!"); }
int main()
{
using arg_type = const typename std::string&;
using ret_type = typename std::string ;
std::vector<std::function<ret_type(arg_type)>> funcs = { f1, f2, f3 };
auto output = std::accumulate(
funcs.cbegin(), funcs.cend(), arg<ret_type>("Hello"),
[](auto a, auto f){
return a.Apply(f);
}).get();
std::cout << output << std::endl;
return 0;
}