從C# Extension Method說起 1 2 3 4 5 6 7 8 9 static class StringUtilities { public static int WordCount(this string text) { return text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Length; } } var text = "This is an example"; var count = text.WordCount();
將一個Method綁在已有的type之上 在C++有類似的提案,叫做UFCS 不過提案會不會過還不知道,但是可以用目前的技術模擬 以下從Reddit看來的兩種方式,寫下自己的看法
Ver 1 使用CRTP技巧來達成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 template <typename T>struct uniform_call_syntax { template <typename F, typename ... Args> constexpr auto call (Args... args) { return F::function(static_cast <T&>(*this ), args...); } }; struct my_type : public uniform_call_syntax<my_type> { int value; }; struct set_value { static my_type& function (my_type& o, int i) { o.value = i; return o; } }; struct add_value { static my_type& function (my_type& o, int i) { o.value += i; return o; } }; struct get_value { static int function (const my_type& o) { return o.value; } }; int test () { my_type obj; return obj .call<set_value>(10 ) .call<add_value>(20 ) .call<get_value>(); }
作法顯而易見,每個需要UFCS功能的都需要繼承uniform_call_syntax,然後每個Externsion method都需要有同樣的function name 缺點也顯而易見,要有這功能就需要繼承,有些type是不允許修改的
Ver 2 版本二需要C++14以上的標準
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <string> #include <iostream> template <typename Arg, typename F>decltype (auto ) operator % (Arg&& arg, F&& fn){ return std ::forward<F>(fn)(std ::forward<Arg>(arg)); } namespace my{ auto tolower = [] { return [](auto && s) -> decltype (auto ) { for (auto & c : s) { c = ('A' <= c && c <= 'Z' ) ? c - ('Z' - 'z' ) : c; } return decltype (s)(s); }; }; auto append (const std ::string & what) { return [&](auto && s) -> decltype (auto ) { s += what; std ::cerr << "is-lvalue-reference:" << std ::is_lvalue_reference<decltype (s)>::value << ";" << s << "\n" ; return std ::forward<decltype (s)>(s); }; }; } int main () { auto s1 = std ::string { "HELLO," } % my::tolower () % my::append(" world" ); auto s2 = std ::string { "GOODOBYE," }; s2% my::tolower () % my::append(" cruel world." ); std ::cerr << s1 << "\n" << s2 << "\n" ; return 0 ; }
看起來很美,很像Functional Programming的Pipe觀念 不過對Programmer的要求比較高,至少要有FP的基本觀念才比較容易理解