C++17 で名前付き引数を書いてみた 〜完全版〜
C++17 で実装されているらしい、user defined literals の機能を使って、C++17 で名前付き引数を書いてみた。
ちなみに不完全版はこっち。
C++17 で名前付き引数書いてみた。C++17 だとこういうこともできる http://t.co/sqoMmQbCGP
— バンビちゃん@実際偽物のC++er (@pink_bangbi) 2015, 6月 17
不完全版は戻り値型を定義する必要があったんですが、完全版では型を定義しなくてもよくしました。
あとデフォルト値も渡せるようにしてみた。
[使い方]
#include <iostream> // 可変長テンプレート引数で引数を受け取る template<typename... Args> auto disp(Args... args){ // get({名前}, args...) を使って値を受け取る std::cout << "x=" << ::get("x"_, args...) << std::endl; std::cout << "y=" << ::get("y"_, args...) << std::endl; // default_({デフォルト値}) でデフォルト値を渡す std::cout << "z=" << ::get("z"_, args..., default_(0.0)) << std::endl; } int main(){ // 名前をつけて引数を渡す // 名前は文字列 + UDL で定義する disp("x"_ = 1, "y"_ = 2); disp("y"_ = 1, "z"_ = 2, "x"_ = 3); // すべてのデフォルト値を渡したり disp("z"_ = 1, default_(42)); }
[出力]
x=1 y=2 z=0 x=3 y=1 z=2 x=42 y=42 z=1
UDL を利用して、文字列を使って名前を定義しています。
C++17 から UDL で文字列が template<typename Char, Char... CS>
で受け取ることができるようになったみたいなのでこんな事ができるようになりました。
C++17 以前で名前付き引数を実装するとなると、どうしても引数毎に型を定義する必要があったんですが、この機能が実装されたことにより UDL をつかってもっと簡単に書くことができるようになりました。
C++17 すごい。
[実装]
#include <tuple> template<typename Char, Char... CS> struct basic_string{ }; template<char... CS> struct string : basic_string<char, CS...>{}; template<typename Name, typename T> struct parameter{ T value; }; template<typename Name> struct name{ template<typename T> auto operator =(T t){ return parameter<name, T>{t}; } }; template<typename Char, Char... CS> auto operator "" _(){ return name<basic_string<Char, CS...>>{}; } template<typename T> struct default_t{ T value; }; template<typename T> auto default_(T t){ return default_t<T>{t}; } template<typename Name, typename... Args> struct find_name_type; template<typename Name> struct find_name_type<Name>{}; template<typename Name, typename T, typename... Args> struct find_name_type<Name, parameter<Name, T>, Args...>{ using type = parameter<Name, T>; }; template<typename Name, typename T, typename... Args> struct find_name_type<Name, default_t<T>, Args...>{ using type = default_t<T>; }; template<typename Name, typename T, typename... Args> struct find_name_type<Name, T, Args...> : find_name_type<Name, Args...>{}; ; template< typename Name, typename... Args, typename Result = typename find_name_type<Name, Args...>::type > auto get(Name, Args... args){ return std::get<Result>(std::make_tuple(args...)).value; } #include <iostream> template<typename... Args> auto disp(Args... args){ std::cout << "x=" << ::get("x"_, args...) << std::endl; std::cout << "y=" << ::get("y"_, args...) << std::endl; std::cout << "z=" << ::get("z"_, args..., default_(0.0)) << std::endl; } int main(){ disp("x"_ = 1, "y"_ = 2); disp("y"_ = 1, "z"_ = 2, "x"_ = 3); disp("z"_ = 1, default_(42)); }
[おまけ]
ちなみに元ネタというかきっかけ(っていうか UDL の機能が実装されていると知ったのは)はここ
これ自体はかなり前に教えてもらって、名前付き引数のコード自体は全然覚えてなかったのだけれど、いざ自分で書いてみたらほとんど書き方が同じだった。
デフォルト値の設定方法はこっちの方がよさそうだなー。