有问题的主要问题是,我想编写一个函数,该函数需要两组可变长度的参数。
我决定采用的抽象是模拟以下调用语法:
f({a,b,c},x,y);
如果a
、b
和c
都具有相同的类型,则可以使其与一起工作
template <typename X, typename... A>
void f(std::initializer_list<X> xs, A&&... as) { . . . }
但是使用std::tuple
而不是std::initializer_list
的类似函数定义不允许使用我想要的语法。
template <typename... X, typename... A>
void f(const std::tuple<X...>& xs, A&&... as) { . . . }
有什么技巧可以让第一组中的类型是异构的吗?
附录:一般来说,我不知道X...
的大小。
附录2:有人知道支撑初始值设定项适用于std::tuple t{a,b,c}
,但不适用于模板参数上下文的技术原因吗?是因为在我的例子中xs
没有被X...
扩展吗?
不,目前使用您想要的语法是不可能的。初始值设定项列表只能推导为数组或std::initializer_list
,这两种类型都是同构类型。
这是一条特殊的规则。初始值设定项列表没有类型,因此无法推导为其他类型。如果函数参数不是数组或std::initializer_list
,则为其提供初始值设定项列表的参数将成为非推导上下文,模板参数推导将忽略它。
特别是类模板参数推导不能在函数参数中完成,以从支持的初始值设定项列表中确定类型,因为它可以在C++17以来的变量定义中完成。这样的功能没有添加到语言中。
原则上,通过使用例如std::variant
的数组作为参数,可以允许有限数量的不同类型,但这只会在运行时解析为类型,不太可能是您想要的。
您需要将类型名称或函数调用添加到初始值设定项列表大括号中,如中所示
f(std::forward_as_tuple(a, b, c), x, y)
或者您可以使用标记类型来指示平面参数列表中第一个包的结束:
f(a, b, c, tag, x, y)
然后可以通过一些模板工作来检测标签。
您想要的目前并不方便,但还有另一种替代方案:
f(a, b, c)(x)(y)();
如果您利用f
不返回任何内容这一事实,您可以返回一个lambda或其他接受进一步参数的函数对象。一个空的参数列表可能意味着,将不会有进一步的参数。
示例:
template <typename ...A>
auto f(A&& ...a)
{
if constexpr (sizeof...(a) > 1)
{
std::tuple(std::forward<A>(a)...);
}
else
{
// burp
}
return [](auto&& ...a)
{
if constexpr (sizeof...(a))
{
return f(std::forward<decltype(a)>(a)...);
}
else
{
std::cout << "done" << std::endl;
}
};
}
int main()
{
f(1, 2, 3)(4)(5)();
return 0;
}
https://wandbox.org/permlink/eAB2S4aCxxiSlMAF
编辑:也试试这个替代方案:
template <typename ...A>
void f(std::any const(&)[3], A&&...)
{
}
int main()
{
f({1, 2u, '3'}, 4., 5.f);
return 0;
}
https://wandbox.org/permlink/0OhoJpQ1YpSGOV2c
您可以使用std::variant
来实现类型安全或比STL提供的更快的功能。当然,您也可以编写自己的容器。
编辑2:然后再说一遍:
template <typename ...A>
void f(std::initializer_list<std::any>, A&&...)
{
}
int main()
{
f({1, 2u, '3'}, 4., 5.f);
return 0;
}
当然,不要使用std::any
,而是使用你自己喜欢的类型。
https://wandbox.org/permlink/rqSVK6aj5zuSFRcf
编辑3:还有另一种方法:
class U
{
void const* const v_;
using typeid_t = void(*)();
typeid_t const tid_;
public:
template <typename T>
static typeid_t type_id() noexcept
{
return typeid_t(U::type_id<T>);
}
template <typename A>
U(A&& a) noexcept: v_(&a), tid_(type_id<std::remove_cvref_t<A>>()) {}
template <typename A>
A const& get() const noexcept { return *static_cast<A const*>(v_); }
auto type_id() const noexcept { return tid_; }
};
template <typename... A>
void f(U const (&a)[3], A&&...)
{
std::cout << a[0].get<double>() << std::endl;
}
int main()
{
f({1.5, 2, 3.f}, 'a', 1);
return 0;
}
您可以将typeid
检查设置为可选,并仅在调试构建中编译它。
https://wandbox.org/permlink/wgXOycB5I3bb3ExV