C++ 推导出通用可变参数模板函数的不完整包

问题描述 投票:0回答:1

给定以下代码:

template <class P>
concept Pair = requires(P pair)
{
    typename P::first_type;
    typename P::second_type;
    pair.first;
    pair.second;
    { pair.first } -> std::same_as<typename P::first_type &>;
    { pair.second } -> std::same_as<typename P::second_type &>;
};

template <typename T>
setTag(const std::string_view key, const T &value)
{
    foo(key.data(), value);
    // things in here
}

template <Pair... Pairs>
void setTags(Pairs... pairs)
{
    ((setTag(std::get<0>(pairs), std::get<1>(pairs)), ...));
}

要调用

setTags
,必须在每个参数包上声明一对:

setTags(std::make_pair("key1", 1),
        std::make_pair("key2", "example"),
        std::make_pair("key3", std::set{1, 2}));

我一直在寻找一种无需显式使用

make_pair
函数即可调用此函数的方法,例如:

setTags({"key1", 1},
        {"key2", "example"},
        {"key3", std::set{1, 2}});

但这会引发以下编译错误:

note: candidate template ignored: substitution failure: deduced incomplete pack <(no value), (no value), (no value)> for template parameter 'Pairs'

这是有道理的,因为编译器无法推断出包中参数的类型。 这就是为什么我引入 C++20 概念以某种方式强制键入,但它不起作用。

一些方法:

  1. 使用 json 库,例如
    nlohmann_json
    并接受 json 参数,但这会引入不需要的依赖性。
  2. 使用
    std::initializer_list
    ,像这样:
template <typename T>
void setTags(std::initializer_list<std::pair<std::string,T>> pairs)
{
    for(const auto &[key, value] : pairs){
        setTag(key, value);
    }
}

但这里的问题是,我们必须指定必须相同的值的类型,从而失去了通用性……

setTags<int>({{"key1", 1}, {"key2",2}});

===编辑===

正如一些人所建议的,这里有一个概念,检查

std::pair
std::get
first
是字符串类型(作为键值标签中的键)

template <typename P>
concept KeyValuePair = requires(P pair) {
    typename std::decay_t<P>::first_type;
    typename std::decay_t<P>::second_type;

    { std::get<0>(pair) } -> std::convertible_to<typename std::decay_t<P>::first_type>;
    { std::get<1>(pair) } -> std::convertible_to<typename std::decay_t<P>::second_type>;

    std::is_convertible_v<typename std::decay_t<P>::first_type, std::string>;
};

和一个较短的版本使用

CTAD

setTags(std::pair("key1", 1),
        std::pair("key2", "example"),
        std::pair("key3", std::set{1, 2}));
c++ generics variadic-templates c++-concepts
1个回答
1
投票

{..}
没有类型,因此不能用
T
(约束与否)推导出来。

一个解决方法可能是压平对:

template <typename T1, typename T2, typename... Ts>
void setTags(T1 t1, T2 t2, Ts... ts)
{
    setTag(t1, t2);
    if constexpr (sizeof...(Ts) > 0) 
    {
        setTags(ts...);
    }
}

随叫随到

setTags("key1", 1,
        "key2", "example",
        "key3", std::set{1, 2});

演示

© www.soinside.com 2019 - 2024. All rights reserved.