tuple操作について
tuple操作について
お世話になっております
C++14 勉強中ですが、
例えば、tupleの中にいろいろobjectを入れてから、ある関数func(STD::tuple t)の引数として渡されて、
func(STD::tuple t)の中で tに保存している要素(object)を一つ一つ取り出して、各要素の型に応じて
設定したり、処理したりします。
いろいろ調べたところ、tuple中の要素を取り出すには std::get<N>( t ); を使うしかないようです。
それに、Nで要素位置を指定しますが、常数値でなければなりません。
例えば、tupleの中に50個の要素が格納されているとすれば、
auto& v0 = std::get<0>( t );
[v0の型を判定して何等かの処理をする]
auto& v1 = std::get<1>( t );
[v1の型を判定して何等かの処理をする]
auto& v2 = std::get<2>( t );
[v2の型を判定して何等かの処理をする]
:: ::
auto& v49 = std::get<49>( t );
[v49の型を判定して何等かの処理をする]
のように一一コードを書かなければなりませんね。
for (int n = 0 ; n<50; n++) {
auto& v = std::get<n>( t ); // 現実この文は無理ですけれども
vの型を判定して何等かの処理をする;
}
のような感覚で処理できる方法はないでしょうか。
C++14 勉強中ですが、
例えば、tupleの中にいろいろobjectを入れてから、ある関数func(STD::tuple t)の引数として渡されて、
func(STD::tuple t)の中で tに保存している要素(object)を一つ一つ取り出して、各要素の型に応じて
設定したり、処理したりします。
いろいろ調べたところ、tuple中の要素を取り出すには std::get<N>( t ); を使うしかないようです。
それに、Nで要素位置を指定しますが、常数値でなければなりません。
例えば、tupleの中に50個の要素が格納されているとすれば、
auto& v0 = std::get<0>( t );
[v0の型を判定して何等かの処理をする]
auto& v1 = std::get<1>( t );
[v1の型を判定して何等かの処理をする]
auto& v2 = std::get<2>( t );
[v2の型を判定して何等かの処理をする]
:: ::
auto& v49 = std::get<49>( t );
[v49の型を判定して何等かの処理をする]
のように一一コードを書かなければなりませんね。
for (int n = 0 ; n<50; n++) {
auto& v = std::get<n>( t ); // 現実この文は無理ですけれども
vの型を判定して何等かの処理をする;
}
のような感覚で処理できる方法はないでしょうか。
- tk-xleader
- 記事: 158
- 登録日時: 13年前
- 連絡を取る:
Re: tuple操作について
boostなしでというのであればこんな感じでしょうかね。
#include <iostream>
#include <tuple>
using namespace std;
template<std::size_t I>
class do_tuple_exec{
template<typename T,std::size_t J>
struct impl{
template<typename Func,typename... Types>
static void exec(std::tuple<Types...>& t, Func f){
f(std::get<J>(t));
impl<T,J+1>::exec(t,f);
}
};
template<typename T>
struct impl<T,I>{
template<typename Func,typename... Types>
static void exec(std::tuple<Types...>& t, Func f){
}
};
public:
template<typename Func,typename... Types>
static void exec(std::tuple<Types...>& t,Func f){
impl<int,0>::exec(t,f);
}
};
template<typename Func,typename... Types>
void tuple_exec(std::tuple<Types...>& t,Func f){
do_tuple_exec<std::tuple_size<std::tuple<Types...>>::value>::exec(t,f);
}
int main() {
std::tuple<int,double,bool> t(100,3.14,true);
tuple_exec(t,[](auto v){std::cout<<v<<std::endl;});
return 0;
}
- tk-xleader
- 記事: 158
- 登録日時: 13年前
- 連絡を取る:
Re: tuple操作について
上のコードの簡単な説明です。
テンプレートでループを実現しようという場合、テンプレートを再帰的に特殊化するというテクニックが一般的に利用されます。そこでテンプレート引数に整数を取るようにして、愚直に可変長テンプレートの数まで再帰し続けるように実装したのが上のコードです。
下のようなコードと同じような発想に基づいています。
テンプレートでループを実現しようという場合、テンプレートを再帰的に特殊化するというテクニックが一般的に利用されます。そこでテンプレート引数に整数を取るようにして、愚直に可変長テンプレートの数まで再帰し続けるように実装したのが上のコードです。
下のようなコードと同じような発想に基づいています。
Re: tuple操作について
新米 さんが書きました: for (int n = 0 ; n<50; n++) {
auto& v = std::get<n>( t ); // 現実この文は無理ですけれども
vの型を判定して何等かの処理をする;
}
のような感覚で処理できる方法はないでしょうか。
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include <typeinfo>
using namespace std;
void func(const int& v)
{
cout << v << " の型は、" << typeid(int).name() << " です。" << endl;
}
void func(const double& v)
{
cout << v << " の型は、" << typeid(double).name() << " です。" << endl;
}
void func(const char*& v)
{
cout << v << " の型は、" << typeid(const char*).name() << " です。" << endl;
}
void func(const string& v)
{
cout << v << " の型は、" << typeid(string).name() << " です。" << endl;
}
template<typename Tuple, typename F, size_t... I>
void for_each_impl(Tuple t, F&& f, index_sequence<I...>)
{
using tasks = int[];
tasks{ (void(std::forward<F>(f)(std::get<I>(t))), 0)... };
}
template<typename Tuple, typename F>
void for_each(Tuple t, F&& f)
{
for_each_impl(t, std::forward<F>(f), make_index_sequence<tuple_size<Tuple>::value>());
}
int main()
{
auto t = make_tuple(7, "hello", 823.75, string("hahaha"));
for_each(t, [](auto& v)
{
func(v);
});
}
Re: tuple操作について
tk-xleader 様、 sleep 様
すごいコードを作って頂いてありがとうございます。
本当に皆さんのコードを拝見して本当に感心致しました。
でも、理解には少々時間かかりますので、後で報告させていただきます。
すごいコードを作って頂いてありがとうございます。
本当に皆さんのコードを拝見して本当に感心致しました。
でも、理解には少々時間かかりますので、後で報告させていただきます。
Re: tuple操作について
tk-xleader 様
参考させていただいたコードの中に、
下記の処にコンパイルエラーが発生しました[Visual Studio 2015]
原因ご存じであれば、教えてくださいませんか。
参考させていただいたコードの中に、
下記の処にコンパイルエラーが発生しました[Visual Studio 2015]
原因ご存じであれば、教えてくださいませんか。
- tk-xleader
- 記事: 158
- 登録日時: 13年前
- 連絡を取る:
Re: tuple操作について
GCCおよびClangでは普通に通りますから、コードそのものに問題があるわけではないはずなのですが、Visual C++は標準準拠度が他に比べると低いといわざるを得ないので、おそらくVisual C++側の問題ではないかと思います。
ただ、こちらでもコンパイルをして、エラーメッセージを確認したところ、どうもdo_tuple_exec::implの実体化の段階で、特殊化されているはずのクラステンプレートが実体化されず、特殊化されていないバージョンが実体化されているようです。おそらくエラーの原因はこれだと思います。
ただ、こちらでもコンパイルをして、エラーメッセージを確認したところ、どうもdo_tuple_exec::implの実体化の段階で、特殊化されているはずのクラステンプレートが実体化されず、特殊化されていないバージョンが実体化されているようです。おそらくエラーの原因はこれだと思います。
Re: tuple操作について
tk-xleader 様へ
さっそくお返答ありがとうございます。
事情了解致しました。
高度なアイディアとプログラミング技法にいい勉強になりました。
sleep 様へ
ご提供いただいたコードは本当に素晴らしいです。
Visual studio 2015でまったく問題なく実行できました。
それにアイディアの高さとプログラミング技法の高さとともに感服しました。
Don't wake up ! (笑)
さっそくお返答ありがとうございます。
事情了解致しました。
高度なアイディアとプログラミング技法にいい勉強になりました。
sleep 様へ
ご提供いただいたコードは本当に素晴らしいです。
Visual studio 2015でまったく問題なく実行できました。
それにアイディアの高さとプログラミング技法の高さとともに感服しました。
Don't wake up ! (笑)
Re: tuple操作について
①は、typedef
②は、int型配列のリスト初期化
②は、std::initializer_list でも代用可
template<typename Tuple, typename F, size_t... I>
void for_each_impl(Tuple t, F&& f, index_sequence<I...>)
{
std::initializer_list<int>{ (void(std::forward<F>(f)(std::get<I>(t))), 0)... };
}
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
#include <typeinfo>
using namespace std;
void func(const int& v)
{
cout << v << " の型は、" << typeid(int).name() << " です。" << endl;
}
void func(const double& v)
{
cout << v << " の型は、" << typeid(double).name() << " です。" << endl;
}
void func(const char*& v)
{
cout << v << " の型は、" << typeid(const char*).name() << " です。" << endl;
}
void func(const string& v)
{
cout << v << " の型は、" << typeid(string).name() << " です。" << endl;
}
int main()
{
auto t = make_tuple(7, "hello", 823.75, string("hahaha"));
auto lambda = [](auto& v) { func(v); };
// using tasks = int[];// -----------①
typedef int tasks[];
// tasks{ (void(std::forward<F>(f)(std::get<I>(t))), 0)... }; // ----------②
tasks // std::initializer_list<int> でも代用可
{
// voidへのキャストは、コンパイラへ常に lambda の戻り値を使用しないことを明示
(static_cast<void>(lambda(std::get<0>(t))), 0),
(static_cast<void>(lambda(std::get<1>(t))), 0),
(static_cast<void>(lambda(std::get<2>(t))), 0),
(static_cast<void>(lambda(std::get<3>(t))), 0)
};
cout << endl;
//②の動作確認
int array[]
{
(func("abcde"), 16),
((void)func(1), 9, "URL", 7, 17),
(void(3), 18),
(123.456, 19)
};
for (auto i : array)
{
cout << i << endl;
}
}
Re: tuple操作について
sleep様
丁寧なご解説ありがとうございます。
ところが、下記の文の意味はなんですか。
もうC言語の範疇を超えたような表現ですね!
出力結果の規律性もわかりません。
すみませんが、もう一度ご説明お願い致します。
出力:
abcde の型は、class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > です。
1 の型は、int です。
16
17
18
19
====================
私の環境:
MS Visual studio 2015
OS Windows7
丁寧なご解説ありがとうございます。
ところが、下記の文の意味はなんですか。
もうC言語の範疇を超えたような表現ですね!
出力結果の規律性もわかりません。
すみませんが、もう一度ご説明お願い致します。
//②の動作確認
int array[]
{
(func("abcde"), 16),
((void)func(1), 9, "URL", 7, 17),
(void(3), 18),
(123.456, 19)
};
出力:
abcde の型は、class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > です。
1 の型は、int です。
16
17
18
19
====================
私の環境:
MS Visual studio 2015
OS Windows7
Re: tuple操作について
C言語でも下記のように修正すれば同じことができます。(オーバーロードが無いのと初期化構文のパターンが少ないので)新米 さんが書きました: もうC言語の範疇を超えたような表現ですね!
#include <stdio.h>
void func(const char* v)
{
printf("%s\n", v);
}
int main()
{
int array[] =
{
(func("abcde"), 16),
((void)func("hello"), 9, "URL", 7, 17),
((void)3, 18),
(123.456, 19)
};
for (int i = 0; i < 4; i++)
{
printf("%d\n", array[i]);
}
}
変数、配列などの初期化を行う構文は言語の基礎中の基礎なので、この辺りは自身で調べてしっかり勉強しておいてください。
は、 のようにも記述できます。
あと、コンマ演算子(Wikipedia)についても、C言語で使用されているものです。
#include <stdio.h>
void func(const char* v)
{
printf("%s\n", v);
}
int main()
{
int a = 0;
a = (1, func("hello"), 3);
printf("a = %d\n", a);
}
Re: tuple操作について
sleep 様
丁寧なご教授に本当に感謝の気持ちいっぱいです。
恥ずかしながら、変数初期化にいろいろ方法があることは知りませんでした。
いい勉強になりました。
ついでにカンマ初期化法に関して
通常関数を入れても良いが、Lambda関数はダメですね。
というか、カンパラしても問題ない、run-timeエラーも出ないが、ただただ無視されるだけみたいです。
例えば、
Lambda関数の中にbとcの値を変更したが、
aの値だけ更新され、bとcは前のままでした。
丁寧なご教授に本当に感謝の気持ちいっぱいです。
恥ずかしながら、変数初期化にいろいろ方法があることは知りませんでした。
いい勉強になりました。
ついでにカンマ初期化法に関して
通常関数を入れても良いが、Lambda関数はダメですね。
というか、カンパラしても問題ない、run-timeエラーも出ないが、ただただ無視されるだけみたいです。
例えば、
Lambda関数の中にbとcの値を変更したが、
aの値だけ更新され、bとcは前のままでした。