假设我有一些 T
类型的对象,并且我想将其放入引用包装器中:
int a = 5, b = 7;
std::reference_wrapper<int> p(a), q(b); // or "auto p = std::ref(a)"
现在我可以轻松地说 if (p ,因为引用包装器有一个到其包装类型的转换。一切都很愉快,我可以处理引用包装器的集合,就像它们是原始对象一样。
(如下面链接的问题所示,这可能是生成现有集合的备用视图的有用方法,可以随意重新排列,而不会产生完整副本的成本,也可以保持原始集合的更新完整性。)
但是,对于某些类,这不起作用:
std::string s1 = "hello", s2 = "world";
std::reference_wrapper<std::string> t1(s1), t2(s2);
return t1 < t2; // ERROR
我的解决方法是定义一个谓词如这个答案*;但我的问题是:
为什么以及何时可以将运算符应用于引用包装器并透明地使用包装类型的运算符?为什么 std::string
失败?它与 std::string
是模板实例有什么关系?
*)更新:根据答案,似乎使用 std::less() 是一种通用解决方案。
Suppose I have some object of type T
, and I want to put it into a reference wrapper:
int a = 5, b = 7;
std::reference_wrapper<int> p(a), q(b); // or "auto p = std::ref(a)"
Now I can readily say if (p < q)
, because the reference wrapper has a conversion to its wrapped type. All is happy, and I can process a collection of reference wrappers just like they were the original objects.
(As the question linked below shows, this can be a useful way to produce an alternate view of an existing collection, which can be rearranged at will without incurring the cost of a full copy, as well as maintaining update integrity with the original collection.)
However, with some classes this doesn't work:
std::string s1 = "hello", s2 = "world";
std::reference_wrapper<std::string> t1(s1), t2(s2);
return t1 < t2; // ERROR
My workaround is to define a predicate as in this answer*; but my question is:
Why and when can operators be applied to reference wrappers and transparently use the operators of the wrapped types? Why does it fail for std::string
? What has it got to do with the fact that std::string
is a template instance?
*) Update: In the light of the answers, it seems that using std::less<T>()
is a general solution.
发布评论
评论(2)
编辑:将我的猜测移到底部,这是为什么这不起作用的规范文本。 TL;DR 版本:
§14.8.3 [温度超过] p1
§14.8.2.1 [temp.deduct.call] p4
§14.8.1 [temp.arg.explicit] p6
由于
std::basic_string
取决于推导的模板参数(CharT
、Traits),不允许转换。
这是一个先有鸡还是先有蛋的问题。要推导模板参数,它需要一个 std::basic_string 的实际实例。要转换为包装类型,需要一个转换目标。该目标必须是实际类型,而类模板则不是。编译器必须针对转换运算符或类似的操作来测试 std::basic_string 的所有可能实例化,这是不可能的。
假设以下最小测试用例:
如果我们不为
int
上的实例化提供重载,则推导失败。如果我们提供该重载,编译器就可以使用允许的用户定义转换(foo const&
是转换目标)来测试它。由于在这种情况下转换匹配,重载解析成功,我们得到了函数调用。Edit: Moved my guesswork to the bottom, here comes the normative text why this won't work. TL;DR version:
§14.8.3 [temp.over] p1
§14.8.2.1 [temp.deduct.call] p4
§14.8.1 [temp.arg.explicit] p6
Since
std::basic_string
depends on deduced template parameters (CharT
,Traits
), no conversions are allowed.This is kind of a chicken and egg problem. To deduce the template argument, it needs an actual instance of
std::basic_string
. To convert to the wrapped type, a conversion target is needed. That target has to be an actual type, which a class template is not. The compiler would have to test all possible instantiations ofstd::basic_string
against the conversion operator or something like that, which is impossible.Suppose the following minimal testcase:
If we don't provide the overload for an instantiation on
int
, the deduction fails. If we provide that overload, it's something the compiler can test against with the one allowed user-defined conversion (foo<int> const&
being the conversion target). Since the conversion matches in this case, overload resolution succeeds and we got our function call.std::reference_wrapper
没有operator<
,因此执行ref_wrapper 的唯一方法是通过
ref_wrapper
code> 成员:如您所知,
std::string
是:string 的相关声明是
:
string 此函数声明模板通过匹配
string
=basic_string
进行实例化,解析为charT< /code> =
char
等。因为
std::reference_wrapper
(或其任何(零)基类)无法匹配basic_string
,函数声明模板不能实例化为函数声明,也不能参与重载。这里重要的是没有非模板
运算符< (字符串,字符串)
原型。显示问题的最小代码
给出:
标准引用
14.8.2.1 从函数调用中推导出模板参数 [temp .deduct.call]
(...)
请注意,
std::string() 就是这种情况。
请参阅下面的评论。
注释
这意味着在本段中:
14.8.1 显式模板参数规范 [temp.arg.explicit]/6
如果不应被视为当且仅当,因为它会直接与前面引用的文本相矛盾。
std::reference_wrapper
does not have anoperator<
, so the only way to doref_wrapper<ref_wrapper
is via theref_wrapper
member:As you know,
std::string
is:The relevant declaration for
string<string
is:For
string<string
this function declaration template is instantiated by matchingstring
=basic_string<charT,traits,Allocator>
which resolves tocharT
=char
, etc.Because
std::reference_wrapper
(or any of its (zero) bases classes) cannot matchbasic_string<charT,traits,Allocator>
, the function declaration template cannot be instantiated into a function declaration, and cannot participate in overloading.What matters here is that there is no non-template
operator< (string, string)
prototype.Minimal code showing the problem
Gives:
Standard citations
14.8.2.1 Deducing template arguments from a function call [temp.deduct.call]
(...)
Note that this is the case with
std::string()<std::string()
.See comment below.
Comment
This implies that in this paragraph:
14.8.1 Explicit template argument specification [temp.arg.explicit]/6
the if should not be taken as a if and only if, as it would directly contradict the text quoted previously.