使用可变参数模板的隐式转换

Implicit conversions with variadic templates

考虑两个函数调用

foo({"a", 1}, {"b", "value"});
foo({"a", 1}, {"b", "value"}, {"c", 1.0});

有没有办法为任意数量的参数对编写函数 foo

我在想一些事情

template <typename... Args>
void foo(std::pair<const char*, Args>&&...);

不幸的是,这不起作用。

gcc 失败并出现错误:

error: too many arguments to function 'void foo(std::pair<const char*, Args>&& ...) [with Args = {}]'
 foo({"aa", 1});

尝试稍微简化您的示例并考虑一下:

#include<utility>

template<typename T>
void foo(std::pair<char*, T>) {}

int main() {
    foo({"a", 1});
}

它没有编译,as you can see
问题是 { "a", 1 } 不是 std::pair,即使您可以从中构造一个,如下所示:

#include<utility>

void foo(std::pair<char*, int>) {}

int main() {
    foo({"a", 1});
}

错误很明显:

couldn't infer template argument 'T'

为什么你不能?

一旦 T 已知,编译器就可以构建这样的一对。无论如何,必须推导 T 而编译器不能这样做,因为 { "a", 1 } 不是可以推导它的对。
无论如何,{ "a", 1 } 可以转换为一对,在特定情况下转换为 std::pair<char *, T> 的特化,但首先必须推导出 T
从什么推导出来的?当然是一对,但你还没有一对。
以此类推,循环。

现在让我们讨论一下您尝试做一些涉及可变参数模板的类似事情:不用说,即使上面显示的更简单的示例不能编译,它的可变参数扩展(如果有的话)也不会编译出于或多或少相同的原因。

Is there a way to write function foo for arbitrary number of argument pairs?

我会说不,除非你使用成对作为 foo 的参数。
它遵循一个最小的工作示例:

#include<utility>

template <typename... Args>
void foo(std::pair<const char*, Args>&&...) {}

int main() {
    foo(std::make_pair("a", 1), std::make_pair("b", "value"));
}

如果你愿意,你也可以推导出第一个参数,只要它的类型是固定的:

#include<utility>

template <typename T, typename... Args>
void foo(std::pair<T, Args>&&...) {}

int main() {
    foo(std::make_pair("a", 1), std::make_pair("b", "value"));
}

不然没修好你可以这样做:

#include<utility>

template <typename... First, typename... Second>
void foo(std::pair<First, Second>&&...) {}

int main() {
    foo(std::make_pair("a", 1), std::make_pair(0, "value"));
}

Is there a way to write function foo for arbitrary number of argument pairs?

有一些基于可变参数模板的解决方案,但参数 必须成对 以允许编译器推断类型。那么这样的事情可能会起作用:

template<typename... Args>
void foo() {}

template<typename T, typename U, typename... Args>
void foo(const std::pair<T, U>& p, Args... args) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    foo(args...);
}

因此:

foo(std::make_pair("a", 1), std::make_pair("b", "value"), std::make_pair("c", 1.0));

输出(clang 3.8)是:

void foo(const std::pair<T, U> &, Args...) [T = const char *, U = int, Args = <std::__1::pair<const char *, const char *>, std::__1::pair<const char *, double>>]
void foo(const std::pair<T, U> &, Args...) [T = const char *, U = const char *, Args = <std::__1::pair<const char *, double>>]
void foo(const std::pair<T, U> &, Args...) [T = const char *, U = double, Args = <>]

Here 是完整的工作示例。

要扩展 ,您可以将配对创建移动到 foo 函数中:

template<typename... Args>
void foo() {}

// Forward declaration
template<typename U, typename... Args>
void foo(const char * str, U u, Args... args);

// When given a pair
template<typename U, typename... Args>
void foo(const std::pair<const char *, U>& p, Args... args) {
    std::cout << p.first << " = " << p.second << std::endl;
    foo(args...);
}

// when given a C string and something else, make a pair
template<typename U, typename... Args>
void foo(const char * str, U u, Args... args) {
    foo(std::make_pair(str, u), args...);
}

然后你可以这样称呼它:

foo("hi", 42,
    "yo", true,
    std::make_pair("Eh", 3.14),
    "foo", false,
    some_pair);

在 c++17 中,您可以通过使用构造函数的模板推导来解决这个问题并稍微欺骗编译器:

#include <iostream>
#include <utility>

template <class... Args>
struct P:std::pair<Args...> {
    P(Args... args):std::pair<Args...>(args...) { }
};

template <class... Args>
void foo(std::pair<const char *, Args>&&...) {
}

int main() {
    foo(P{"abc", 1}, P{"abc", "abc"}, P{"abc", 2.0});
}

[live demo]