上一个问题:std::string类继承和繁琐的c++重载解析
在上一个问题之后的步骤中,我尝试在原始字符串指针"aaa" + path_string{ "bbb" }
上测试operator+
。并发现它没有调用相应的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)
将调用而不是比(1)
匹配得更好的(2)
。
这是怎么回事?
PS:我认为这与上一个问题中关于const
+single reference
的问题相同。
在以下片段中:
std::string test_path_string_operator_plus_right_xref(path_string && right_path_str)
{
return "aaa" + right_path_str;
}
right_path_str
是一个非常值。正因为如此,它永远无法绑定到右值引用。
当模板重载不可用时,它绑定到中的常量左值引用重载
friend path_basic_string operator+ (const t_elem * p, const base_type & r)
当模板存在时,它更适合于非常量左值引用:
template <typename T>
friend path_basic_string operator+ (const t_elem * p, T && r)
在这种情况下,T&&
是一个转发引用,它折叠为非常值引用。要修复您的代码,请确保在调用函数时从右值引用参数中使用move
,并使其成为一种习惯——无论何时传递下面的右值引用,都要使用std::move
,无论何时传递转发引用,都必须使用std::forward
。
std::string test_path_string_operator_plus_right_xref(path_string && right_path_str)
{
return "aaa" + std::move(right_path_str);
}