[英]Pass initializer lists and ranges seamlessly to a function parameter
I am trying to create a function that will initialize an internal std::set<std::string>
, and I want to expose an API that allows any type of ranges as input, including initializer lists.我正在尝试创建一个 function 来初始化一个内部std::set<std::string>
,我想公开一个 API 允许任何类型的范围作为输入,包括初始化列表。 So what I'm aiming for is:所以我的目标是:
// API definition
class Toto
{
public:
void set_modes(??? modes_) {
this->modes = {modes_.begin(), modes_.end()};
}
private:
std::set<std::string> modes;
};
// Expected API usage
Toto instance;
instance.set_modes({"super", "ultra"});
const std::vector<std::string> other_modes {"top", "notch"};
instance.set_modes(other_modes);
What I've tried is:我试过的是:
template<std::ranges::input_range Range>
void set_modes(Range&& modes_)
{
this->modes = {modes_.begin(), modes_.end()};
}
and it works fine with an instantiated container, but it fails with a temporary initializer list.它与实例化容器一起工作正常,但它因临时初始化列表而失败。 The error is the following (Clang 16.0):错误如下(Clang 16.0):
error: no matching member function for call to 'set_modes'
note: candidate template ignored: couldn't infer template argument 'Range'
set_modes(Range&& modes)
^
My guess is that I somehow I have to tell my function that I only want ranges of std::string_view
, but I'm not sure how to do that.我的猜测是我必须以某种方式告诉我的 function 我只想要std::string_view
的范围,但我不确定该怎么做。 Or am I asking for something impossible to do in the first place?还是我一开始就要求做一些不可能的事情? Thanks谢谢
[EDIT] [编辑]
According to this answer C++ class initializable from any sequence type initializer lists are always aa special case so I guess I need an overload.根据这个答案C++ class 可从任何序列类型初始化列表初始化总是一个特殊情况所以我想我需要一个重载。 Answer is 5 years old though, so except if someone tells me that there is some c++20 / c++23 witchcraft that can handle that in a single function, I'll mark this thread as resolved.虽然答案是 5 岁,所以除非有人告诉我有一些 c++20/c++23 巫术可以在一个 function 中处理它,否则我会将此线程标记为已解决。
My guess is that I somehow I have to tell my function that I only want ranges of std::string_view, but I'm not sure how to do that.我的猜测是我必须以某种方式告诉我的 function 我只想要 std::string_view 的范围,但我不确定该怎么做。
If you want to support {"super", "ultra"}
, you can default the template parameter Range
to initializer_list<string_view>
.如果要支持{"super", "ultra"}
,可以将模板参数Range
默认为initializer_list<string_view>
。 And for other ranges, you can constrain its value type to be convertible to string_view
, which can be obtained through range_value_t
.对于其他范围,您可以将其值类型限制为可转换为string_view
,这可以通过range_value_t
。
Also, you may need to constrain Range
to common_range
, since std::set
's iterator-pair constructor expects both iterators to be of the same type:此外,您可能需要将Range
限制为common_range
,因为std::set
的迭代器对构造函数期望两个迭代器属于同一类型:
class Toto
{
public:
template<std::ranges::input_range Range =
std::initializer_list<std::string_view>>
requires std::ranges::common_range<Range> &&
std::convertible_to<std::ranges::range_value_t<Range>,
std::string_view>
void set_modes(Range&& modes_) {
this->modes = {modes_.begin(), modes_.end()};
}
private:
std::set<std::string> modes;
};
You could use an array initializer like this: (same for a set_modes function)您可以使用这样的数组初始值设定项:(与 set_modes 函数相同)
#include <iostream>
#include <set>
#include <string>
class Toto
{
public:
template<std::size_t N>
Toto(const std::string(&modes)[N]) :
m_modes{ std::begin(modes), std::end(modes) }
{
}
private:
std::set<std::string> m_modes{};
};
int main()
{
Toto toto{{"mode1", "mode2"}};
return 0;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.