构造 std::tuple 时定义元素初始化顺序
Defining element initialization order when constructing std::tuple
我想将元组中元素的初始化值存储在单独的元组中,以便我可以使用相同的值作为相应类型的其他元组的预设。
以从左到右的顺序调用元组元素的构造函数对我的程序非常重要(否则它充其量会变得非常混乱)。
这是我的程序的简化版本:
#include <tuple>
// Elements are the end points of a Widget hierarchy
struct Element
{
using initer_t = int;
Element( const initer_t pIniter )
:data{ pIniter }
{
printf("Creating %i\n", data);
}
const initer_t data;
};
// A Widget class stores any number of Elements and/or other Widget instances
template<typename... Elems>
struct Widget
{
using initer_t = std::tuple<typename Elems::initer_t...>;
Widget( const initer_t pIniter )
:elements{ pIniter }
{}
const std::tuple<Elems...> elements;
};
int main()
{
using Button = Widget<Element, Element>;
using ButtonList = Widget<Button, Button, Element>;
Button::initer_t basic_button_initer{ 0, 1 }; // presets for Buttons
Button::initer_t other_button_initer{ 2, 3 };
ButtonList::initer_t buttonlist_initer{ basic_button_initer, other_button_initer, 4 }; //a preset for a ButtonList
ButtonList buttonlist{ buttonlist_initer };
return 0;
}
所以我在 Widget<Elems...>
的构造函数初始化列表中用 std::tuple<typename Elems::initer_t...>
初始化 Widget<Elems...>
的 std::tuple<Elems...> elements
成员。
这应该使用 pIniter 中的值使用 initer_t
定义的类型的相应初始化值来初始化 elements
的每个元素。
initer_t
类型是 Widget 层次结构的每个成员的类型(例如 Widget<typename...>
或 Element
),这是层次结构成员应该初始化的类型。
但是发生这种情况的顺序是从右到左,而我需要它是从左到右。
程序的输出是
Creating 4
Creating 3
Creating 2
Creating 1
Creating 0
但是我想颠倒这个顺序。
在这种情况下我该怎么做?
标准中对std::tuple
成员初始化的顺序没有要求,恐怕。
您可以按特定顺序遍历 tuple
,例如:
#include <tuple>
#include <iostream>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
int main()
{
auto a = std::make_tuple(true, 42, 3.14, "abc");
boost::fusion::for_each(a, [](auto& value) {
std::cout << value << '\n';
});
}
输出:
1
42
3.14
abc
对于任何对解决方案感兴趣的人,我想出了一种方法来控制初始化顺序并保留 elements
:
的常量
#include <tuple>
template<typename... Elems>
struct construct
{
template<size_t... Ns, typename Head, typename... Rest>
static constexpr const std::tuple<Rest...>
drop_head_impl( const std::index_sequence<Ns...> ns,
const std::tuple<Head, Rest...> tup )
{
return std::tuple<Rest...>( std::get<Ns + 1u>( tup )... );
}
template<typename Head, typename... Rest>
static constexpr const std::tuple<Rest...>
drop_head( const std::tuple<Head, Rest...> tup )
{
return drop_head_impl( std::make_index_sequence<sizeof...(Rest)>(), tup );
}
template<typename Head>
static constexpr const std::tuple<Head>
func_impl( const std::tuple<typename Head::initer_t> initer )
{
return std::tuple<Head>( { std::get<0>( initer ) } );
}
template<typename Head, typename Next, typename... Rest>
static constexpr const std::tuple<Head, Next, Rest...>
func_impl( const std::tuple<typename Head::initer_t, typename Next::initer_t, typename Rest::initer_t...> initer )
{
std::tuple<Head> head( { std::get<0>( initer ) } );
return std::tuple_cat( head, func_impl<Next, Rest...>( drop_head(initer) ) );
}
static constexpr const std::tuple<Elems...>
func( const std::tuple<typename Elems::initer_t...> initer )
{
return func_impl<Elems...>( initer );
}
};
// Elements are the end points of a Widget hierarchy
struct Element
{
using initer_t = int;
Element( const initer_t pIniter )
:data{ pIniter }
{
printf( "Creating %i\n", data );
}
const initer_t data;
};
// A Widget class stores any number of Elements and/or other Widget instances
template<typename... Elems>
struct Widget
{
using initer_t = std::tuple<typename Elems::initer_t...>;
Widget( const initer_t pIniter )
:elements( construct<Elems...>::func( pIniter ) )
{}
const std::tuple<Elems...> elements;
};
int main()
{
using Button = Widget<Element, Element>;
using ButtonList = Widget<Button, Button, Element>;
Button::initer_t basic_button_initer{ 0, 1 }; // presets for Buttons
Button::initer_t other_button_initer{ 2, 3 };
ButtonList::initer_t buttonlist_initer{ basic_button_initer, other_button_initer, 4 }; //a preset for a ButtonList
ButtonList buttonlist{ buttonlist_initer };
return 0;
}
construct
结构接受initer_t
s(initer)的元组,使用initer的第一个元素构造一个包含Elems...
第一个元素的元组,然后丢弃第一个initer 的元素并将剩余的元组传递给自身,这导致使用 initer 中的下一个元素构造具有 Elems...
的下一个元素的元组。对于具有一个元素的元组,此递归被 func_impl
的重载停止,该元素简单地从元组中的 initer_t
和 returns 构造该元素。这个 single-element 元组连接到具有前一个元素的元组,结果返回到更高级别并连接到那里的 single-element 元组,依此类推。
我想将元组中元素的初始化值存储在单独的元组中,以便我可以使用相同的值作为相应类型的其他元组的预设。
以从左到右的顺序调用元组元素的构造函数对我的程序非常重要(否则它充其量会变得非常混乱)。
这是我的程序的简化版本:
#include <tuple>
// Elements are the end points of a Widget hierarchy
struct Element
{
using initer_t = int;
Element( const initer_t pIniter )
:data{ pIniter }
{
printf("Creating %i\n", data);
}
const initer_t data;
};
// A Widget class stores any number of Elements and/or other Widget instances
template<typename... Elems>
struct Widget
{
using initer_t = std::tuple<typename Elems::initer_t...>;
Widget( const initer_t pIniter )
:elements{ pIniter }
{}
const std::tuple<Elems...> elements;
};
int main()
{
using Button = Widget<Element, Element>;
using ButtonList = Widget<Button, Button, Element>;
Button::initer_t basic_button_initer{ 0, 1 }; // presets for Buttons
Button::initer_t other_button_initer{ 2, 3 };
ButtonList::initer_t buttonlist_initer{ basic_button_initer, other_button_initer, 4 }; //a preset for a ButtonList
ButtonList buttonlist{ buttonlist_initer };
return 0;
}
所以我在 Widget<Elems...>
的构造函数初始化列表中用 std::tuple<typename Elems::initer_t...>
初始化 Widget<Elems...>
的 std::tuple<Elems...> elements
成员。
这应该使用 pIniter 中的值使用 initer_t
定义的类型的相应初始化值来初始化 elements
的每个元素。
initer_t
类型是 Widget 层次结构的每个成员的类型(例如 Widget<typename...>
或 Element
),这是层次结构成员应该初始化的类型。
但是发生这种情况的顺序是从右到左,而我需要它是从左到右。
程序的输出是
Creating 4
Creating 3
Creating 2
Creating 1
Creating 0
但是我想颠倒这个顺序。
在这种情况下我该怎么做?
标准中对std::tuple
成员初始化的顺序没有要求,恐怕。
您可以按特定顺序遍历 tuple
,例如:
#include <tuple>
#include <iostream>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
int main()
{
auto a = std::make_tuple(true, 42, 3.14, "abc");
boost::fusion::for_each(a, [](auto& value) {
std::cout << value << '\n';
});
}
输出:
1
42
3.14
abc
对于任何对解决方案感兴趣的人,我想出了一种方法来控制初始化顺序并保留 elements
:
#include <tuple>
template<typename... Elems>
struct construct
{
template<size_t... Ns, typename Head, typename... Rest>
static constexpr const std::tuple<Rest...>
drop_head_impl( const std::index_sequence<Ns...> ns,
const std::tuple<Head, Rest...> tup )
{
return std::tuple<Rest...>( std::get<Ns + 1u>( tup )... );
}
template<typename Head, typename... Rest>
static constexpr const std::tuple<Rest...>
drop_head( const std::tuple<Head, Rest...> tup )
{
return drop_head_impl( std::make_index_sequence<sizeof...(Rest)>(), tup );
}
template<typename Head>
static constexpr const std::tuple<Head>
func_impl( const std::tuple<typename Head::initer_t> initer )
{
return std::tuple<Head>( { std::get<0>( initer ) } );
}
template<typename Head, typename Next, typename... Rest>
static constexpr const std::tuple<Head, Next, Rest...>
func_impl( const std::tuple<typename Head::initer_t, typename Next::initer_t, typename Rest::initer_t...> initer )
{
std::tuple<Head> head( { std::get<0>( initer ) } );
return std::tuple_cat( head, func_impl<Next, Rest...>( drop_head(initer) ) );
}
static constexpr const std::tuple<Elems...>
func( const std::tuple<typename Elems::initer_t...> initer )
{
return func_impl<Elems...>( initer );
}
};
// Elements are the end points of a Widget hierarchy
struct Element
{
using initer_t = int;
Element( const initer_t pIniter )
:data{ pIniter }
{
printf( "Creating %i\n", data );
}
const initer_t data;
};
// A Widget class stores any number of Elements and/or other Widget instances
template<typename... Elems>
struct Widget
{
using initer_t = std::tuple<typename Elems::initer_t...>;
Widget( const initer_t pIniter )
:elements( construct<Elems...>::func( pIniter ) )
{}
const std::tuple<Elems...> elements;
};
int main()
{
using Button = Widget<Element, Element>;
using ButtonList = Widget<Button, Button, Element>;
Button::initer_t basic_button_initer{ 0, 1 }; // presets for Buttons
Button::initer_t other_button_initer{ 2, 3 };
ButtonList::initer_t buttonlist_initer{ basic_button_initer, other_button_initer, 4 }; //a preset for a ButtonList
ButtonList buttonlist{ buttonlist_initer };
return 0;
}
construct
结构接受initer_t
s(initer)的元组,使用initer的第一个元素构造一个包含Elems...
第一个元素的元组,然后丢弃第一个initer 的元素并将剩余的元组传递给自身,这导致使用 initer 中的下一个元素构造具有 Elems...
的下一个元素的元组。对于具有一个元素的元组,此递归被 func_impl
的重载停止,该元素简单地从元组中的 initer_t
和 returns 构造该元素。这个 single-element 元组连接到具有前一个元素的元组,结果返回到更高级别并连接到那里的 single-element 元组,依此类推。