传递大括号列表参数时调用可变参数函数模板的问题

n. *_* m. 15 c++ variadic-templates c++11 list-initialization

考虑这个功能模板:

template <class... T>
void foo (std::tuple<T, char, double> ... x);
Run Code Online (Sandbox Code Playgroud)

此调用有效:

using K = std::tuple<int, char, double>;
foo ( K{1,'2',3.0}, K{4,'5',6.0}, K{7,'8',9.0} );
Run Code Online (Sandbox Code Playgroud)

这个没有:

foo ( {1,'2',3.0}, {4,'5',6.0}, {7,'8',9.0} );
Run Code Online (Sandbox Code Playgroud)

(gcc和clang都抱怨过多论据foo)

为什么第二次调用有问题?我可以重写声明,foo以便第二次通话也被接受吗?

模板参数T仅用于实现可变参数.实际类型是已知和固定的,只有参数的数量不同.在现实生活中,类型不同int, char, double,这只是一个例子.

我不能使用C++ 17.更喜欢C++ 11兼容的解决方案.

Pio*_*cki 9

生成一组重载的构造函数:

#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});
}
Run Code Online (Sandbox Code Playgroud)

DEMO


生成一组重载的函数调用操作符:

#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});
}
Run Code Online (Sandbox Code Playgroud)

演示2


创建(或使用预处理器宏生成)一组重载,将参数转发给单个实现:

#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});
}
Run Code Online (Sandbox Code Playgroud)

演示3


作为数组传递并推断其大小(需要额外的一对parens):

#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 ~~~~~^
}
Run Code Online (Sandbox Code Playgroud)

演示4


使用an std::initializer_list作为构造函数参数(跳过额外的parens):

#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} };
}
Run Code Online (Sandbox Code Playgroud)

演示5