Cppreferenceのstd::visit()
ページを見ると、 https://en.cppreference.com/w/cpp/utility/variant/visit で、作成できないコードに遭遇しましたの感覚...
短縮版は次のとおりです。
_#include <iomanip>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
#include <vector>
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...)->overloaded<Ts...>;
int main() {
std::vector<std::variant<int,long,double,std::string>> vec = { 10, 15l, 1.5, "hello" };
for (auto& v : vec) {
std::visit(overloaded{
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}, v);
}
}
_
int main()
のすぐ上のoverloaded
を宣言する2行はどういう意味ですか?
説明してくれてありがとう!
2019追加
以下の2人の紳士が詳細な説明を提供してくれて(ありがとうございました!)、非常に素晴らしい本C++ 17の詳細-エキサイティングな機能を学んでください新しいC++標準!BartłomiejFilipekによる。このようなよく書かれた本!
Int main()のすぐ上のオーバーロードを宣言する2行はどういう意味ですか?
最初の1つ
_template<class... Ts>
struct overloaded : Ts...
{ using Ts::operator()...; };
_
古典的なクラス/構造体宣言/定義/実装です。 C++ 11から有効(可変長テンプレートを使用するため)。
この場合、overloaded
はすべてのテンプレートパラメーターから継承し、継承されたすべてのoperator()
を有効にします(using
行)。これは Variadic CRTP の例です。
残念ながら、可変個のusing
はC++ 17以降でのみ使用できます。
二番目
_template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
_
「控除ガイド」(詳細は このページ を参照)であり、新しいC++ 17機能です。
あなたの場合、控除ガイドはあなたが何かを書くとき
_auto ov = overloaded{ arg1, arg2, arg3, arg4 };
_
またはまた
_overloaded ov{ arg1, args, arg3, arg4 };
_
ov
はoverloaded<decltype(arg1), decltype(arg2), decltype(arg3), decltype(arg4)>
になります
これにより、何かを書くことができます
_overloaded
{
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
}
_
c ++ 14では
_auto l1 = [](auto arg) { std::cout << arg << ' '; };
auto l2 = [](double arg) { std::cout << std::fixed << arg << ' '; };
auto l3 = [](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
overloaded<decltype(l1), decltype(l2), decltype(l3)> ov{l1, l2, l3};
_
-編集-
質問のサンプルコードでNemo(ありがとう!)が指摘したように、別の興味深い新しいC++ 17機能があります:基本クラスの集約初期化です。
つまり...書くとき
_overloaded
{
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
}
_
3つのラムダ関数を渡して、overloaded
の3つの基本クラスを初期化しています。
C++ 17より前は、明示的なコンストラクターを作成して初めてこれを行うことができました。 C++ 17以降、自動的に動作します。
この時点で、C++ 17でのoverloaded
の簡略化された完全な例と、対応するC++ 14の例を示すことは有用であると思われます。
私は次のC++ 17プログラムを提案します
_#include <iostream>
template <typename ... Ts>
struct overloaded : public Ts ...
{ using Ts::operator()...; };
template <typename ... Ts> overloaded(Ts...) -> overloaded<Ts...>;
int main ()
{
overloaded ov
{
[](auto arg) { std::cout << "generic: " << arg << std::endl; },
[](double arg) { std::cout << "double: " << arg << std::endl; },
[](long arg) { std::cout << "long: " << arg << std::endl; }
};
ov(2.1);
ov(3l);
ov("foo");
}
_
そして、私が想像できる最高のC++ 14の代替(「make」関数のbolovの提案と彼の再帰overloaded
の例にも従います)。
_#include <iostream>
template <typename ...>
struct overloaded;
template <typename T0>
struct overloaded<T0> : public T0
{
template <typename U0>
overloaded (U0 && u0) : T0 { std::forward<U0>(u0) }
{ }
};
template <typename T0, typename ... Ts>
struct overloaded<T0, Ts...> : public T0, public overloaded<Ts ...>
{
using T0::operator();
using overloaded<Ts...>::operator();
template <typename U0, typename ... Us>
overloaded (U0 && u0, Us && ... us)
: T0{std::forward<U0>(u0)}, overloaded<Ts...> { std::forward<Us>(us)... }
{ }
};
template <typename ... Ts>
auto makeOverloaded (Ts && ... ts)
{
return overloaded<Ts...>{std::forward<Ts>(ts)...};
}
int main ()
{
auto ov
{
makeOverloaded
(
[](auto arg) { std::cout << "generic: " << arg << std::endl; },
[](double arg) { std::cout << "double: " << arg << std::endl; },
[](long arg) { std::cout << "long: " << arg << std::endl; }
)
};
ov(2.1);
ov(3l);
ov("foo");
}
_
それは意見の問題だと思いますが、C++ 17バージョンははるかにシンプルでエレガントなようです。
ああ、私はこれが大好きです。
テンプレート引数呼び出し演算子のセットにオーバーロードされた呼び出し演算子で構造体を簡潔に宣言する方法です。
_template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
_
overloaded
は_Ts...
_を継承し、operator()
のすべてを使用します
_template<class... Ts> overloaded(Ts...)->overloaded<Ts...>;
_
これは推論ガイドであるため、テンプレートパラメータを指定しないでください。
使用方法は、例で示したとおりです。
複数のラムダ(およびその他の関数型)のオーバーロードされたセットを作成するのに便利なユーティリティです。
C++ 17以前では、overload
を作成するには再帰を使用する必要がありました。きれいではない:
_template <class... Fs> struct Overload : Fs...
{
};
template <class Head, class... Tail>
struct Overload<Head, Tail...> : Head, Overload<Tail...>
{
Overload(Head head, Tail... tail)
: Head{head}, Overload<Tail...>{tail...}
{}
using Head::operator();
using Overload<Tail...>::operator();
};
template <class F> struct Overload<F> : F
{
Overload(F f) : F{f} {}
using F::operator();
};
template <class... Fs> auto make_overload_set(Fs... fs)
{
return Overload<Fs...>{fs...};
}
auto test()
{
auto o = make_overload_set(
[] (int) { return 24; },
[] (char) { return 11; });
o(2); // returns 24
o('a'); // return 11
}
_
主な厄介な点は、継承は集約ではないためOverload
であるため、再帰トリックを実行してすべてのタイプのコンストラクターを作成する必要があることです。 C++ 17では、overloaded
は(はい)集合体なので、1つを構築するとすぐに機能します:)。また、それぞれにusing::operator()
を指定する必要があります。