默认从索引构建 std::variant
Default constructing an std::variant from index
当索引仅在 运行 时已知时,从所需类型的索引默认构造 std::variant 的最简单方法是什么?换句话说,我想写:
const auto indx = std::variant<types...>{someobject}.index();
//...somewhere later, indx having been passed around...
std::variant<types...> var = variant_from_index(indx);
///var is now set to contain a default constructed someobject
请注意,indx
不能成为 constexpr,因此 std::in_place_index
在这里不起作用。
这里的问题当然是因为不知道在编译时调用 types...
中的哪个构造函数,所以基本上是 table 所有可能的构造函数(或者可能是默认构造的变体)复制自)必须在编译时构建,然后在 运行 时访问。这里显然有一些模板魔法,但最干净的方法是什么?
我尝试了以下 (on coliru),但索引序列似乎出现错误(最后打印出 2 0 0
),我很困惑为什么:
编辑: 它按照下面的固定方式工作,我的 constexpr 数组初始化错误。所以现在的问题是,有没有更简洁的方法来做到这一点?
#include <variant>
#include <iostream>
using var_t = std::variant<int, float, const char *>;
//For debug
template<class ...types>
struct WhichType;
template<class T, class U>
struct default_variants;
template<class...Params, std::size_t... I>
struct default_variants<std::variant<Params...>, std::index_sequence<I...>> {
using variant_t = std::variant<Params...>;
//Uncomment to see the index sequence
//WhichType<std::index_sequence<I...>> idx{};
constexpr static variant_t variants[sizeof...(Params)]{variant_t{std::in_place_index<I>}...};
constexpr static std::size_t indices[sizeof...(Params)]{I...};
};
template<class T>
struct default_variants_builder;
template<class...Params>
struct default_variants_builder<std::variant<Params...>> {
using indices = std::make_index_sequence<sizeof...(Params)>;
using type = default_variants<std::variant<Params...>, indices>;
};
int main() {
using builder_t = typename default_variants_builder<var_t>::type;
var_t floatvar{1.2f};
var_t variant2 = builder_t::variants[floatvar.index()];
std::cout << "Contained " << floatvar.index() << "; Now contains " << variant2.index() << "\n";
}
不确定这是否非常优雅,但我认为它有效:
#include <variant>
#include <iostream>
template<typename V, std::size_t N = std::variant_size_v<V>>
struct variant_by_index {
V make_default(std::size_t i) {
if (i >= std::variant_size_v<V>) {
throw std::invalid_argument("bad type index.");
}
constexpr size_t index = std::variant_size_v<V> - N;
if (i == index) {
return std::variant_alternative_t<index, V>();
} else {
return variant_by_index<V, N - 1>().make_default(i);
}
}
};
template<typename V>
struct variant_by_index<V, 0> {
V make_default(std::size_t i) {
throw std::bad_variant_access("bad type index.");
}
};
using var_t = std::variant<int, float, const char *>;
int main() {
variant_by_index<var_t> type_indexer;
var_t my_var_0 = type_indexer.make_default(0);
std::cout << "my_var_0 has int? " << std::holds_alternative<int>(my_var_0) << "\n";
var_t my_var_1 = type_indexer.make_default(1);
std::cout << "my_var_1 has float? " << std::holds_alternative<float>(my_var_1) << "\n";
try {
var_t my_var_1 = type_indexer.make_default(3);
} catch(const std::bad_variant_access&) {
std::cout << "Could not create with type 3.\n";
}
return 0;
}
我相信(有点)优雅的方法可能是使用更通用的习惯用法在 运行 时间选择数字模板参数值,如这个问题中所讨论的:
foo
函数将 std::get<std::size_t I>
(或捕获变体且不带参数的 lambda)。
这个怎么样?
template <class Variant, std::size_t I = 0>
Variant variant_from_index(std::size_t index) {
if constexpr(I >= std::variant_size_v<Variant>)
throw std::runtime_error{"Variant index " + std::to_string(I + index) + " out of bounds"};
else
return index == 0
? Variant{std::in_place_index<I>}
: variant_from_index<Variant, I + 1>(index - 1);
}
对于 Boost.Mp11 这基本上是一个单行(一如既往):
template <typename V>
auto variant_from_index(size_t index) -> V
{
return mp_with_index<mp_size<V>>(index,
[](auto I){ return V(std::in_place_index<I>); });
}
您对问题的描述是准确的——您需要一种将运行时索引转换为编译时索引的方法。 mp_with_index
为你做 - 你给它运行时索引和最大编译时索引(mp_size<V>
这里,如果你愿意的话,它会给出与 std::variant_size_v<V>
相同的值)和它将调用您提供的具有正确常量的函数(I
此处的类型为 integral_constant<size_t, index>
,除了 index
是常量表达式)。
当索引仅在 运行 时已知时,从所需类型的索引默认构造 std::variant 的最简单方法是什么?换句话说,我想写:
const auto indx = std::variant<types...>{someobject}.index();
//...somewhere later, indx having been passed around...
std::variant<types...> var = variant_from_index(indx);
///var is now set to contain a default constructed someobject
请注意,indx
不能成为 constexpr,因此 std::in_place_index
在这里不起作用。
这里的问题当然是因为不知道在编译时调用 types...
中的哪个构造函数,所以基本上是 table 所有可能的构造函数(或者可能是默认构造的变体)复制自)必须在编译时构建,然后在 运行 时访问。这里显然有一些模板魔法,但最干净的方法是什么?
我尝试了以下 (on coliru),但索引序列似乎出现错误(最后打印出 2 0 0
),我很困惑为什么:
编辑: 它按照下面的固定方式工作,我的 constexpr 数组初始化错误。所以现在的问题是,有没有更简洁的方法来做到这一点?
#include <variant>
#include <iostream>
using var_t = std::variant<int, float, const char *>;
//For debug
template<class ...types>
struct WhichType;
template<class T, class U>
struct default_variants;
template<class...Params, std::size_t... I>
struct default_variants<std::variant<Params...>, std::index_sequence<I...>> {
using variant_t = std::variant<Params...>;
//Uncomment to see the index sequence
//WhichType<std::index_sequence<I...>> idx{};
constexpr static variant_t variants[sizeof...(Params)]{variant_t{std::in_place_index<I>}...};
constexpr static std::size_t indices[sizeof...(Params)]{I...};
};
template<class T>
struct default_variants_builder;
template<class...Params>
struct default_variants_builder<std::variant<Params...>> {
using indices = std::make_index_sequence<sizeof...(Params)>;
using type = default_variants<std::variant<Params...>, indices>;
};
int main() {
using builder_t = typename default_variants_builder<var_t>::type;
var_t floatvar{1.2f};
var_t variant2 = builder_t::variants[floatvar.index()];
std::cout << "Contained " << floatvar.index() << "; Now contains " << variant2.index() << "\n";
}
不确定这是否非常优雅,但我认为它有效:
#include <variant>
#include <iostream>
template<typename V, std::size_t N = std::variant_size_v<V>>
struct variant_by_index {
V make_default(std::size_t i) {
if (i >= std::variant_size_v<V>) {
throw std::invalid_argument("bad type index.");
}
constexpr size_t index = std::variant_size_v<V> - N;
if (i == index) {
return std::variant_alternative_t<index, V>();
} else {
return variant_by_index<V, N - 1>().make_default(i);
}
}
};
template<typename V>
struct variant_by_index<V, 0> {
V make_default(std::size_t i) {
throw std::bad_variant_access("bad type index.");
}
};
using var_t = std::variant<int, float, const char *>;
int main() {
variant_by_index<var_t> type_indexer;
var_t my_var_0 = type_indexer.make_default(0);
std::cout << "my_var_0 has int? " << std::holds_alternative<int>(my_var_0) << "\n";
var_t my_var_1 = type_indexer.make_default(1);
std::cout << "my_var_1 has float? " << std::holds_alternative<float>(my_var_1) << "\n";
try {
var_t my_var_1 = type_indexer.make_default(3);
} catch(const std::bad_variant_access&) {
std::cout << "Could not create with type 3.\n";
}
return 0;
}
我相信(有点)优雅的方法可能是使用更通用的习惯用法在 运行 时间选择数字模板参数值,如这个问题中所讨论的:
foo
函数将 std::get<std::size_t I>
(或捕获变体且不带参数的 lambda)。
这个怎么样?
template <class Variant, std::size_t I = 0>
Variant variant_from_index(std::size_t index) {
if constexpr(I >= std::variant_size_v<Variant>)
throw std::runtime_error{"Variant index " + std::to_string(I + index) + " out of bounds"};
else
return index == 0
? Variant{std::in_place_index<I>}
: variant_from_index<Variant, I + 1>(index - 1);
}
对于 Boost.Mp11 这基本上是一个单行(一如既往):
template <typename V>
auto variant_from_index(size_t index) -> V
{
return mp_with_index<mp_size<V>>(index,
[](auto I){ return V(std::in_place_index<I>); });
}
您对问题的描述是准确的——您需要一种将运行时索引转换为编译时索引的方法。 mp_with_index
为你做 - 你给它运行时索引和最大编译时索引(mp_size<V>
这里,如果你愿意的话,它会给出与 std::variant_size_v<V>
相同的值)和它将调用您提供的具有正确常量的函数(I
此处的类型为 integral_constant<size_t, index>
,除了 index
是常量表达式)。