给定以下代码:
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 概念以某种方式强制键入,但它不起作用。
一些方法:
nlohmann_json
并接受 json 参数,但这会引入不需要的依赖性。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}));
{..}
没有类型,因此不能用 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});