【发布时间】:2019-04-10 02:22:31
【问题描述】:
上一个问题:std::string class inheritance and tedious c++ overload resolution
在上一个问题之后的步骤中,我尝试通过原始字符串指针测试operator+:"aaa" + path_string{ "bbb" }。发现并没有调用相应的path_string类友元函数。
我尝试添加不模板重载operator+(2),但它也没有工作。但我发现模板化的 (3) 确实有效。
#include <string>
template <class t_elem, class t_traits, class t_alloc>
class path_basic_string : public std::basic_string<t_elem, t_traits, t_alloc>
{
public:
using base_type = std::basic_string<t_elem, t_traits, t_alloc>;
path_basic_string() = default;
path_basic_string(const path_basic_string & ) = default;
path_basic_string(path_basic_string &&) = default;
path_basic_string & operator =(path_basic_string path_str)
{
this->base_type::operator=(std::move(path_str));
return *this;
}
path_basic_string(base_type r) :
base_type(std::move(r))
{
}
path_basic_string(const t_elem * p) :
base_type(p)
{
}
base_type & str()
{
return *this;
}
const base_type & str() const
{
return *this;
}
using base_type::base_type;
using base_type::operator=;
// ... all over operators are removed as not related to the issue ...
// (1)
friend path_basic_string operator+ (const t_elem * p, const base_type & r)
{
path_basic_string l_path = p;
l_path += "xxx";
return std::move(l_path);
}
friend path_basic_string operator+ (const t_elem * p, base_type && r)
{
if (!r.empty()) {
return "111" + ("/" + r); // call base operator instead in case if it is specialized for this
}
return "111";
}
// (2)
friend path_basic_string operator+ (const t_elem * p, path_basic_string && r)
{
base_type && r_path = std::move(std::forward<base_type>(r));
if (!r_path.empty()) {
return "222" + ("/" + r_path); // call base operator instead in case if it is specialized for this
}
return "222";
}
// (3) required here to intercept the second argument
template <typename T>
friend path_basic_string operator+ (const t_elem * p, T && r)
{
base_type && r_path = std::move(std::forward<base_type>(r));
if (!r_path.empty()) {
return "333" + ("/" + r_path); // call base operator instead in case if it is specialized for this
}
return "333";
}
};
using path_string = path_basic_string<char, std::char_traits<char>, std::allocator<char> >;
std::string test_path_string_operator_plus_right_xref(path_string && right_path_str)
{
return "aaa" + right_path_str;
}
int main()
{
const path_string test =
test_path_string_operator_plus_right_xref(std::move(path_string{ "bbb" }));
printf("-%s-\n", test.str().c_str());
return 0;
}
3 个编译器的输出:gcc 5.4、clang 3.8.0、msvc 2015 (19.00.23506)
-333/bbb-
https://rextester.com/BOFUS59590
我记得 C++ 标准对此进行了澄清,因为只有当没有一个非模板化函数与参数完全匹配时,才必须查找模板化函数。但是 (2) 运算符必须完全匹配,但为什么它甚至没有被调用?
如果删除(3),则(1) 将调用而不是(2),后者比(1) 匹配更好。
这是怎么回事?
PS:我认为这与 const + single reference 的问题相同,就像上一个问题一样。
【问题讨论】:
-
请注意
std::move(std::forward<T>(...))是多余的,std::move(...)应该给出相同的结果。无论哪种方式,您总是会得到一个右值引用。在您的运营商的上下文中,std::move和std::forward都不做任何事情。我不清楚混淆的确切位置,但您误解了移动语义的工作原理。 -
@FrançoisAndrieux
std::forward将引用转换为对不同类型(在本例中为基类)的引用,然后std::move将转发引用更改为右值引用,是吗?可能只是static_cast<base_type &&>(r)。 -
@bipll 我不确定你在问什么。但是
base_type从来都不是引用类型,所以std::forward不会执行任何完美转发(T被完全忽略)。添加std::move保证,您将通过始终移动(如果不能编译则无法编译)来忽略完美转发。编辑:要回复对您的评论的编辑,该演员基本上只是std::move。 -
我的问题是
why (3) calls instead of (2)? -
什么是对引用的引用?
标签: c++ c++11 gcc visual-studio-2015 gcc5