c++ - 传递大括号初始化器列表参数时调用可变参数函数模板的问题
问题描述
考虑这个函数模板:
template <class... T>
void foo (std::tuple<T, char, double> ... x);
此调用有效:
using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );
这个没有:
foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
(gcc 和 clang 都抱怨太多的论点foo
)
为什么第二次调用有问题?我可以重写声明foo
以便第二次调用也被接受吗?
模板参数 T 仅用于实现可变性。实际类型是已知且固定的,只有参数的数量不同。在现实生活中,类型与 不同int, char, double
,这只是一个例子。
我不能为此使用 C++17。C++11 兼容的解决方案是首选。
解决方案
生成一组重载的构造函数:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::initializer;
initializer(indexed<T, Is>... ts)
{
// ts is a pack of std::tuple<int, char, double>
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...> {};
using foo = initializer<std::tuple<int, char, double>, 20>;
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
生成一组重载的函数调用运算符:
#include <tuple>
#include <cstddef>
template <typename T, std::size_t M>
using indexed = T;
template <typename T, std::size_t M, std::size_t... Is>
struct initializer : initializer<T, M, sizeof...(Is) + 1, Is...>
{
using initializer<T, M, sizeof...(Is) + 1, Is...>::operator();
int operator()(indexed<T, Is>... ts) const
{
// ts is a pack of std::tuple<int, char, double>
return 1;
}
};
template <typename T, std::size_t M, std::size_t... Is>
struct initializer<T, M, M, Is...>
{
int operator()() const { return 0; }
};
static constexpr initializer<std::tuple<int, char, double>, 20> foo = {};
// tuples limit+1 ~~~^
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
创建(或使用预处理器宏生成)一组将参数转发给单个实现的重载:
#include <array>
#include <tuple>
using K = std::tuple<int, char, double>;
void foo(const std::array<K*, 5>& a)
{
// a is an array of at most 5 non-null std::tuple<int, char, double>*
}
void foo(K p0) { foo({&p0}); }
void foo(K p0, K p1) { foo({&p0, &p1}); }
void foo(K p0, K p1, K p2) { foo({&p0, &p1, &p2}); }
void foo(K p0, K p1, K p2, K p3) { foo({&p0, &p1, &p2, &p3}); }
void foo(K p0, K p1, K p2, K p3, K p4) { foo({&p0, &p1, &p2, &p3, &p4}); }
int main()
{
foo({1,'2',3.0});
foo({1,'2',3.0}, {4,'5',6.0});
foo({1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0});
}
作为数组传递并推断其大小(需要额外的一对括号):
#include <tuple>
#include <cstddef>
template <std::size_t N>
void foo(const std::tuple<int, char, double> (&a)[N])
{
// a is an array of exactly N std::tuple<int, char, double>
}
int main()
{
foo({{1,'2',3.0}, {4,'5',6.0}});
// ^~~~~~ extra parens ~~~~~^
}
使用 anstd::initializer_list
作为构造函数参数(跳过额外的括号):
#include <tuple>
#include <initializer_list>
struct foo
{
foo(std::initializer_list<std::tuple<int, char, double>> li)
{
// li is an initializer list of std::tuple<int, char, double>
}
};
int main()
{
foo{ {1,'2',3.0}, {4,'5',6.0} };
}
推荐阅读
- java - 为什么我的 springboot 应用程序看不到 @Service 注释?
- python - 跟踪字符串中删除的字符数,python`strip()`
- c# - Use Linq to create an inner join on data from SQL Server and Excel
- c++ - Flight Finder C++ unordered_map
- javascript - Loosing Event Handlers on Table elements in jQuery after AJAX
- ruby-on-rails - Create associations in RoR
- oracle - ora-03147 缺少必填的 TTC 字段
- javascript - 我希望 php 在刷新页面时重定向到我设置的目录
- java - 根据 JSR-310 本地化 YearWeek 的良好设计
- python - scrapy编码数据文本python