这是C++ 0x右值引用和临时值的后续问题
在上一个问题中,我询问了这段代码应该如何工作:
void f(const std::string &); //less efficient
void f(std::string &&); //more efficient
void g(const char * arg)
{
f(arg);
}
Run Code Online (Sandbox Code Playgroud)
由于隐式临时似乎应该调用移动重载,这种情况发生在GCC中,而不是MSVC(或MSVC的Intellisense中使用的EDG前端).
这段代码怎么样?
void f(std::string &&); //NB: No const string & overload supplied
void g1(const char * arg)
{
f(arg);
}
void g2(const std::string & arg)
{
f(arg);
}
Run Code Online (Sandbox Code Playgroud)
看来,基于我之前问题的答案,该函数g1是合法的(并且被GCC 4.3-4.5接受,但不被MSVC接受).但是,GCC和MSVC都拒绝,g2因为第13.3.3.1.4/3条禁止左值绑定到rvalue ref参数.我理解这背后的基本原理 - 在N2831"使用右值参考修复安全问题"中对此进行了解释.我也认为GCC可能正如该论文的作者所预期的那样实施该条款,因为GCC的原始补丁是由其中一位作者(Doug Gregor)撰写的.
但是,我不是很直观.对我来说,(a)a const string &在概念上更接近于string &&a const char *,而且(b)编译器可以创建一个临时字符串g2,就好像它是这样编写的:
void g2(const std::string & …Run Code Online (Sandbox Code Playgroud) 是否有理由std::type_info指定为多态?析构函数被指定为虚拟(并且在"C++的设计和演变"中对"因此它是多态的"效果进行了评论).我真的没有看到令人信服的理由.我没有任何具体的用例,我只是想知道它背后是否有任何理由或故事.
以下是我提出并拒绝的一些想法:
dynamic_cast一个std::type_info到另一个,实现定义派生类型.这可能是原因,但似乎实现添加实现定义的成员(可能是虚拟的)同样容易.希望测试这些扩展的程序无论如何都必然是不可移植的.delete使用基指针时正确销毁派生类型.但目前还没有标准的派生类型,用户不能定义有用的派生类型,因为type_info没有标准的公共构造函数,所以delete荷兰国际集团一个type_info指针是从未合法和便携.并且派生类型没有用,因为它们无法构造 - 我知道这种不可构造的派生类型的唯一用途是在is_polymorphic类型特征之类的实现中.class A都会得到派生的"元类" A__type_info,它来源于type_info.也许这样的派生类可以暴露new A以类型安全的方式调用各种构造函数参数的成员,以及类似的东西.但type_info实际上制作多态本身实际上使得这样的想法基本上无法实现,因为你必须为你的元类创建元类,无限制,如果所有type_info对象都有静态存储持续时间,这就是一个问题.也许禁止这是使其变为多态的原因.dynamic_cast)应用于std::type_info自身,或者有人认为它很可爱,或者如果type_info不是多态的则令人尴尬.但鉴于没有标准派生类型,并且标准层次结构中没有其他类可以合理地尝试交叉投射,问题是:什么?是否有用于表达式typeid(std::type_info) == typeid(typeid(A))?type_info,因此便携式程序将无法分辨其中的不同之处.type_info如果type_info保证是虚拟的,也许实现者可以以可移植的方式识别他们自己的子类(而不是来自另一个供应商的子类).最后一个对我来说是最合理的,但它相当弱.
(我在comp.std.c ++上询问了这个问题的变体,但没有得到答案.)
为什么f(arg)在这段代码中的调用调用const ref重载f?
void f(const std::string &); //less efficient
void f(std::string &&); //more efficient
void g(const char * arg)
{
f(arg);
}
Run Code Online (Sandbox Code Playgroud)
我的直觉说f(string &&)应该选择重载,因为arg无论如何都需要转换为临时值,并且临时匹配rvalue引用比lvalue引用更好.
这不是GCC和 MSVC中发生的事情(编辑:谢谢Sumant:它不会发生在GCC 4.3-4.5中).至少在G ++和 MSVC中,任何左值都不会绑定到右值引用参数,即使创建了一个中间临时值.实际上,如果不存在const ref重载,编译器会诊断出错误.然而,编写f(arg + 0) 或f(std::string(arg)) 不选择右值引用过载如你所愿.
从我对C++ 0x标准的阅读中,似乎在考虑是否f(string &&)可行时应考虑将const char*隐式转换为字符串,就像传递const lvalue ref参数时一样.第13.3节(重载解析)在很多地方没有区分rvalue refs和const引用.此外,似乎阻止左值绑定到右值引用(13.3.3.1.4/3)的规则不适用,如果存在中间临时值 - 毕竟,从临时值移动是完全安全的.
这是:
编辑:我有一个相关的后续问题:C++ 0x rvalue引用 - lvalues-rvalue绑定
我们可以在函数内部定义一个本地类,但是这个类不能是一个有点烦人且不一致的模板.在C++ 0x标准中是否有任何更新?
我想部分专门化一个现有的模板,我无法改变(std::tr1::hash)基类和所有派生类.原因是我使用奇怪的重复模板模式进行多态,并且哈希函数在CRTP基类中实现.如果我只想部分专门用于CRTP基类,那么它很简单,我可以写:
namespace std { namespace tr1 {
template <typename Derived>
struct hash<CRTPBase<Derived> >
{
size_t operator()(const CRTPBase<Derived> & base) const
{
return base.hash();
}
};
} }
Run Code Online (Sandbox Code Playgroud)
但是这种专门化只与实际的派生类不匹配CRTPBase<Derived>.我想要的是一种编写部分特化的方法,Derived当且仅当它来源于CRTPBase<Derived>.我的伪代码是
namespace std { namespace tr1 {
template <typename Derived>
struct hash<typename boost::enable_if<std::tr1::is_base_of<CRTPBase<Derived>, Derived>,
Derived>::type>
{
size_t operator()(const CRTPBase<Derived> & base) const
{
return base.hash();
}
};
} }
Run Code Online (Sandbox Code Playgroud)
......但是,这并不工作,因为编译器不能告诉大家,enable_if<condition, Derived>::type是Derived.如果我可以更改std::tr1::hash,我只需添加另一个虚拟模板参数boost::enable_if,如enable_if文档所建议的那样,但这显然不是一个很好的解决方案.有没有解决这个问题的方法?我是否必须在每个unordered_set或 …