提问人:enoon.erehwon 提问时间:8/25/2023 最后编辑:enoon.erehwon 更新时间:8/25/2023 访问量:79
是否可以使用非常量变量和 as_const() 从元组中检索元素?
Is it possible to retrieve an element from a tuple using a non-const variable and as_const()?
问:
正如标题所述,我想知道是否可以将变量传递给元组?std::get<>()
我有一个头文件,其中包含一个结构,其中包含许多用于实例化不同对象类型的参数(和函数)。头文件本质上是一个帮助程序,用于在我测试不同的类时减少实例化中的冗余。
我正在尝试调整定义/初始化函数,以允许使用集合和可变元组将自定义参数(非默认值)传递到其中。该集表示与参数关联的索引,元组将包含要替换到参数中的非默认值。如果集合中存在索引(与参数隐式关联),则使用传入的值代替默认值。
我有一个滚动索引,用于跟踪元组中要从中检索元素的下一个位置。每当从元组中检索项时,索引都会更新。i
使用滚动索引的原因是,其中一些对象在其构造函数中吸收了许多参数 (>10),但我通常只需要通过传入 1-3 个无效参数来测试(预期的)非名义行为。因此,对我来说,理想的情况是不必传入一个大小为参数数的元组(并用占位符填充未被替换的值)。虽然这样可以避免“动态”计算下一个索引的需要,因为它将是 1:1 映射,但这对我来说有点不优雅。i
下面是我的代码的简化版本:
#include <iostream>
#include <tuple>
#include <utility>
#include <set>
struct MyStruct
{
int my_int;
char my_char;
std::string my_string;
template <typename...T>
void initParams(std::set<int> paramIndices=std::set<int>(), std::tuple<T...> customParams=std::tuple<T...>())
{
auto i=0;
// size of set and tuple must match
if(paramIndices.size()==std::tuple_size<std::tuple<T...>>{})
{
// if values are present, substitute them
// these throw an error, which is understandable; i is not const, const template argument is required for std::get<>
my_int = paramIndices.contains(0) ? std::get<i++>(customParams) : 0;
my_char = paramIndices.contains(1) ? std::get<i++>(customParams) : 'a';
my_string = paramIndices.contains(2) ? std::get<i++>(customParams) : "hello";
// attempt to use as_const() to introduce const-ness
// why do these throw an error as well?
my_int = paramIndices.contains(0) ? std::get<std::as_const(i++)>(customParams) : 0;
my_char = paramIndices.contains(1) ? std::get<std::as_const(i++)>(customParams) : 'a';
my_string = paramIndices.contains(2) ? std::get<std::as_const(i++)>(customParams) : "hello";
}
}
/* do stuff with params
...
*/
};
int main()
{
std::set<int> s{0, 2};
std::tuple t{500, "world"};
MyStruct().initParams(s, t);
}
我将 CMake 用于更大的项目,但对于这个简化版本,我编译为:g++ -std=c++20 test.cpp
as_const
是在 C++17 中引入的,set 方法 contains()
是在 C++20 中引入的。
答:
as_const
只需通过添加限定符来修改类型。它不返回编译时常量,而编译时常量是模板参数所必需的。标准中定义的术语常量表达式表示“编译时”常量:const
常量表达式可以在翻译过程中计算
从 https://eel.is/c++draft/expr.const#note-1
也就是说,尽管您的代码无法按编写的方式工作,但一些递归 constexpr 函数元编程应该能够执行将参数分配给成员的整体任务。
评论
您可以使用 std::index_sequence
而不是传递编译时索引:std::set
template <std::size_t ... Is, typename...Ts>
void initParams(std::index_sequence<Is...>, std::tuple<Ts...> customParams)
{
static_assert(sizeof...(Is) == sizeof...(Ts));
auto members = std::tie(my_int, my_char, my_string);
std::apply([&](const auto&... args){ ((std::get<Is>(members) = args), ...) ; }, customParams);
}
评论
也许如果我明白你要做的是不必为你不想初始化的所有参数输入值。如果是这样,那么使用 C++20 您可以这样做:
#include <cassert>
#include <optional>
#include <string>
using namespace std::string_literals;
struct MyStruct
{
std::optional<int> int1{};
std::optional<int> int2{};
std::optional<std::string> string1{};
std::optional<std::string> string2{};
};
int main()
{
MyStruct s{ .int1 = 42, .string2 = "Hello" };
assert(s.int1.value() == 42);
assert(s.int2.has_value() == false);
assert(s.string1.has_value() == false);
assert(*s.string2 == "Hello"s);
return 0;
}
评论
std::index_sequence
评论
get<i++>
???将编译时构造(模板参数)与运行时变量混合是行不通的。std::tuple 是一个辅助类,主要用于(元)模板编程。对于运行时代码,只需使用名称良好的成员定义自己的结构即可。(该结构可以是 constexpr,因此您可以创建这些结构的编译时 std::array)as_const