gpt4 book ai didi

C++ 参数包,受限于单一类型的实例?

转载 作者:IT老高 更新时间:2023-10-28 13:00:30 24 4
gpt4 key购买 nike

从 C++11 开始,我们可以制作可以接受任何参数序列的模板函数:

template <typename... Ts>
void func(Ts &&... ts) {
step_one(std::forward<Ts>(ts)...);
step_two(std::forward<Ts>(ts)...);
}

但是,假设只有在每个参数具有相同类型的情况下调用我的函数才有意义——不过,任何数量的参数都可以。

最好的方法是什么,即在这种情况下,是否有一种好的方法可以约束模板以发出很好的错误消息,或者理想情况下,消除 func当参数不匹配时参与重载决议?


如果有帮助,我可以把它具体化:

假设我有一些结构:

struct my_struct {
int foo;
double bar;
std::string baz;
};

现在,我希望能够做一些事情,例如打印结构的成员以进行调试、序列化和反序列化结构、按顺序访问结构的成员等。我有一些代码可以帮助解决这个问题:

template <typename V>
void apply_visitor(V && v, my_struct & s) {
std::forward<V>(v)("foo", s.foo);
std::forward<V>(v)("bar", s.bar);
std::forward<V>(v)("baz", s.baz);
}

template <typename V>
void apply_visitor(V && v, const my_struct & s) {
std::forward<V>(v)("foo", s.foo);
std::forward<V>(v)("bar", s.bar);
std::forward<V>(v)("baz", s.baz);
}

template <typename V>
void apply_visitor(V && v, my_struct && s) {
std::forward<V>(v)("foo", std::move(s).foo);
std::forward<V>(v)("bar", std::move(s).bar);
std::forward<V>(v)("baz", std::move(s).baz);
}

(生成这样的代码看起来有点费力,但我前段时间创建了 a small library 来帮助解决这个问题。)

所以,现在我想扩展它,使它可以访问 my_struct 的两个实例。同时。它的用途是,如果我想实现相等或比较操作怎么办。在 boost::variant他们称之为“二元访问”的文档与“一元访问”形成对比。

可能没有人愿意做比二进制访问更多的事情。但是假设我想这样做,一般 n-ary探视。然后,我猜是这样的

template <typename V, typename ... Ss>
void apply_visitor(V && v, Ss && ... ss) {
std::forward<V>(v)("foo", (std::forward<Ss>(ss).foo)...);
std::forward<V>(v)("bar", (std::forward<Ss>(ss).bar)...);
std::forward<V>(v)("baz", (std::forward<Ss>(ss).baz)...);
}

但是现在,它变得更加松散了——如果有人传递了一系列甚至根本不是相同结构类型的类型,代码仍然可以编译并执行用户完全意想不到的事情。

我想过这样做:

template <typename V, typename ... Ss>
void apply_visitor(V && v, Ss && ... ss) {
auto foo_ptr = &my_struct::foo;
std::forward<V>(v)("foo", (std::forward<Ss>(ss).*foo_ptr)...);
auto bar_ptr = &my_struct::bar;
std::forward<V>(v)("bar", (std::forward<Ss>(ss).*bar_ptr)...);
auto baz_ptr = &my_struct::baz;
std::forward<V>(v)("baz", (std::forward<Ss>(ss).*baz_ptr)...);
}

如果他们将它与不匹配的类型一起使用,至少会导致编译错误。但是,它也发生得太晚了——它发生在模板类型被解析之后,我猜是在重载解析之后。

我考虑过使用 SFINAE,而不是返回 void,而是使用 std::enable_if_t并检查一些表达式 std::is_same<std::remove_cv_t<std::remove_reference_t<...>>对于参数包中的每种类型。

但是一方面,SFINAE 表达式非常复杂,另一方面,它也有一个缺点——假设有人有一个派生类 struct my_other_struct : my_struct { ... } ,并且他们想将它与访问者机制一起使用,所以其中一些参数是my_struct有些是my_other_struct .理想情况下,系统会将所有引用转换为 my_struct并以这种方式应用访问者,然后我在上面给出的示例中使用成员指针 foo_ptr , bar_ptr , baz_ptr会在那里做正确的事情,但我什至不清楚如何使用 SFINAE 编写这样的约束——我必须尝试找到我猜想的所有参数的共同基础?

有什么好的方法可以调和这些担忧吗?

最佳答案

使用 std::common_type,这很简单:

template <class... Args, class = std::common_type_t<Args...>>
void foo(Args &&... args) {

}

这只会保证C++17 开始对 SFINAE 友好。 ClangGCC 都已经以这种方式实现了。

关于C++ 参数包,受限于单一类型的实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38528801/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com