>最近,我决定编写一个类,存储带有 reference_wrapperstd::variant<vector<string>, reference_wrapper<const vector<string>>>
.
有趣的部分是变体根据初始化存储的内容。 我做了一个小调查,结果发现,在所有情况下,vector<string>
类型都获胜,除了通过 std::cref 传递的情况。这同样适用于函数(有点意料之中,因为构造函数以这种方式类似于函数)
void f(vector<string>); // #1
void f(reference_wrapper<const vector<string>>); // #2
vector<string> data;
const vector<string>& get_data();
f(data); // #1
f(std::cref(data)) // #2
f(get_data()); // #1
f(std::cref(get_data())) // #2
问题是为什么vector<string>
在这里具有优先级。我在这里查看了最佳可行函数部分,但它没有多大意义。似乎,
4) or, if not that, F1 is a non-template function while F2 is a template specialization
部分选择vector<string>
而不是reference_wrapper<vector<string>>
(因为reference_wrapper
构造函数是模板化的),但我不确定,因为我无法完全理解它们是否使用规则相等
1) There is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2
有人可以描述每种情况下应用的所有隐式转换,并显示首选一个重载而不是另一个重载的真正原因吗?对我来说,它们如下:
f(data) = f(vector<string>&) -> (*exact match* implicit conversion) -> f(vector<string>)
f(data) = f(vector<string>&) -> (*conversion* implicit conversion) -> f(reference_wrapper<vector<string>>)
我错过了什么吗?
另一个问题,与此主题相关:隐式转换序列的排名部分再次,这里留下了一个问题,T(const T&)
被认为是完全匹配(用户定义的类类型到同一类的转换)还是转换?
首先,尽管std::reference_wrapper
是标准库的一部分,但它被视为用户定义类型。
例如,从std::vector &
到const std::vector &
的隐式转换始终优于从std::vector&
到std::reference_wrapper<vector>
的隐式转换。这是因为(根据标准)前者是标准转换,但后者是用户定义的转换。第一个称为标准转换,因为它为您的类型添加了const
,但第二个被视为将某种类型转换为完全不同的类型。
检查此代码并查看 cppreference.com。
其次,我试图猜测一些好的选择。我看到您希望存储对向量的引用或移动/(尽可能便宜地复制)或者(您可以说直接初始化)类中的数据,如果它尚未(安全地)存储在某个变量中。也许您可以考虑使用移动语义。你可以在这里玩代码
using TVar = std::variant<reference_wrapper<const vector<string>>, vector<string>>;
class Config {
private:
TVar data;
public:
const vector<string>& get_data() const{
if (data.index() == 1)
return get<1>(data);
else return get<0>(data);
}
Config(const vector<string>& it):data(cref(it)){}
Config(vector<string>&& it):data(move(it)){}
};
这里我们有两个功能。
- 一个引用"储值"(更准确地说是左值)的方法。 将其包装在 cref 中,以便使变体中的
reference_wrapper
替代方案成为最佳重载。 - 另一个做魔术。 它是对直接写入的值(也称为 pvalues )和使用魔术
std::move
函数的值(也称为 xvalues )的引用。 如果你从未见过这个,请参考这个受人尊敬的问答 什么是移动语义?
catch(...)
:),就是这样。 另请注意,您不需要std::monostate
,因为这仅在使变体默认可构造(没有参数)时才需要。 你可以像这样使类默认可构造。
Config(vector<string>&& it = {}):data(move(it)){}
绝对没有理由存储reference_wrapper
任何东西。只需像任何理智的程序员一样使用指针即可。reference_wrapper
用于正确触发std::invoke
和关联的类/函数,如thread
和bind
。