std :: string类继承和繁琐的c ++重载解析 [英] std::string class inheritance and tedious c++ overload resolution

查看:156
本文介绍了std :: string类继承和繁琐的c ++重载解析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要扩展std::basic_string以处理路径字符串和其他operator+:

I need to extend std::basic_string to work over path strings and different operator+:

#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 & operator =(const path_basic_string &) = default;

    path_basic_string(const base_type & r) :
        base_type(r)
    {
    }

    path_basic_string(base_type && r) :
        base_type(std::move(r))
    {
    }
};

using path_string = path_basic_string<char, std::char_traits<char>, std::allocator<char> >;

template <class t_elem, class t_traits, class t_alloc>
inline path_basic_string<t_elem, t_traits, t_alloc> &&
    operator +(
        path_basic_string<t_elem, t_traits, t_alloc> && l,
        std::basic_string<t_elem, t_traits, t_alloc> && r)
{
    std::basic_string<t_elem, t_traits, t_alloc> && l_str = std::move(l);
    std::basic_string<t_elem, t_traits, t_alloc> && r_str = std::move(r);

    const bool has_right = !r_str.empty();
    return std::move(
        path_basic_string<t_elem, t_traits, t_alloc>{
            std::move(std::move(l_str) + (has_right ? "/" : "") + (has_right ? std::move(r_str) : std::move(std::basic_string<t_elem, t_traits, t_alloc>{})))
        });
}

template <class t_elem, class t_traits, class t_alloc>
inline path_basic_string<t_elem, t_traits, t_alloc>
    operator +(
        const path_basic_string<t_elem, t_traits, t_alloc> & l,
        const std::basic_string<t_elem, t_traits, t_alloc> & r)
{
    const std::basic_string<t_elem, t_traits, t_alloc> & l_str = l;

    const bool has_right = !r.empty();
    return path_basic_string<t_elem, t_traits, t_alloc>{
        l_str + (has_right ? "/" : "") + (has_right ? r : std::basic_string<t_elem, t_traits, t_alloc>{})
    };
}

int main()
{
    path_string a;
    std::string b;
    std::string c;
    const path_string test = a + (b + c);

    return 0;
}

https://godbolt.org/z/jhcWoh 中,我遇到了这些错误:

At the https://godbolt.org/z/jhcWoh i've got these errors:

x86 MSVC 19 2015 U3 :

/opt/compiler-explorer/windows/19.00.24210/include/xlocale(341):
warning C4530: C++ exception handler used, but unwind semantics are
not enabled. Specify /EHsc

<source>(61): error C2666: 'operator +': 3 overloads have similar
conversions

<source>(44): note: could be
'path_basic_string<char,std::char_traits<char>,std::allocator<char>>
operator +<char,std::char_traits<char>,std::allocator<char>>(const
path_basic_string<char,std::char_traits<char>,std::allocator<char>>
&,const
std::basic_string<char,std::char_traits<char>,std::allocator<char>>
&)'

<source>(28): note: or      
'path_basic_string<char,std::char_traits<char>,std::allocator<char>>
&&operator
+<char,std::char_traits<char>,std::allocator<char>>(path_basic_string<char,std::char_traits<char>,std::allocator<char>> &&,std::basic_string<char,std::char_traits<char>,std::allocator<char>>
&&)'

/opt/compiler-explorer/windows/19.00.24210/include/xstring(2310):
note: or      
'std::basic_string<char,std::char_traits<char>,std::allocator<char>>
std::operator
+<char,std::char_traits<char>,std::allocator<char>>(const std::basic_string<char,std::char_traits<char>,std::allocator<char>>
&,const
std::basic_string<char,std::char_traits<char>,std::allocator<char>>
&)'

/opt/compiler-explorer/windows/19.00.24210/include/xstring(2380):
note: or      
'std::basic_string<char,std::char_traits<char>,std::allocator<char>>
std::operator
+<char,std::char_traits<char>,std::allocator<char>>(const std::basic_string<char,std::char_traits<char>,std::allocator<char>>
&,std::basic_string<char,std::char_traits<char>,std::allocator<char>>
&&)'

/opt/compiler-explorer/windows/19.00.24210/include/xstring(2390):
note: or      
'std::basic_string<char,std::char_traits<char>,std::allocator<char>>
std::operator
+<char,std::char_traits<char>,std::allocator<char>>(std::basic_string<char,std::char_traits<char>,std::allocator<char>> &&,const
std::basic_string<char,std::char_traits<char>,std::allocator<char>>
&)'

/opt/compiler-explorer/windows/19.00.24210/include/xstring(2400):
note: or      
'std::basic_string<char,std::char_traits<char>,std::allocator<char>>
std::operator
+<char,std::char_traits<char>,std::allocator<char>>(std::basic_string<char,std::char_traits<char>,std::allocator<char>> &&,std::basic_string<char,std::char_traits<char>,std::allocator<char>>
&&)'

<source>(61): note: while trying to match the argument list
'(path_string,
std::basic_string<char,std::char_traits<char>,std::allocator<char>>)'

<source>(61): note: note: qualification adjustment (const/volatile)
may be causing the ambiguity

Compiler returned: 2

x86-64 gcc 5.4 (带有--std=c++11):

source>: In function 'int main()':

<source>:61:40: warning: ISO C++ says that these are ambiguous, even
though the worst conversion for the first is better than the worst
conversion for the second:

     const path_string test = a + (b + c);

                                        ^

<source>:44:5: note: candidate 1: path_basic_string<t_elem, t_traits,
t_alloc> operator+(const path_basic_string<t_elem, t_traits,
t_alloc>&, const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&)
[with t_elem = char; t_traits = std::char_traits<char>; t_alloc =
std::allocator<char>]

     operator +(

     ^

In file included from
/opt/compiler-explorer/gcc-5.4.0/include/c++/5.4.0/string:52:0,

                 from <source>:1:

/opt/compiler-explorer/gcc-5.4.0/include/c++/5.4.0/bits/basic_string.h:4854:5:
note: candidate 2: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>
std::operator+(const std::__cxx11::basic_string<_CharT, _Traits,
_Alloc>&, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]

     operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,

     ^

Compiler returned: 0

我知道至少有一种解决方法.

I know at least one workaround for that.

但是地狱到底是怎么回事? 为了避免这种超载碰撞混乱,我还要再次超载的荒谬之处是什么?

But the hell is what even happened? What in the ridiculousness i have to overload again additionally to avoid that overload collision mess?

更新: 通过从所有operator+的所有basic_string类参数中删除constsingle reference进行修复.似乎可以解决问题.

Update: Fixed by removing const and single reference from all basic_string kind arguments of all operator+. Seems that works it out.

推荐答案

首先,请使用move-from-value代替const&&&重载.

First things first, use move-from-value instead of const& and && overloads.

path_basic_string(base_type r) :
    base_type(std::move(r))
{
}

并摆脱base_type const& ctor.

第二,使该ctor明确:

Second, make that ctor explicit:

explicit path_basic_string(base_type r) :
    base_type(std::move(r))
{
}

因为路径与字符串是不同的东西.

as a path is a different thing than a string.

第三,清理您的template operator+并将其设置为ADL"Koenig"运算符,该运算符按其左手边的值进行操作.哦,不要通过右值引用返回任何有毒的东西.

Third, clean up your template operator+ and make it an ADL "Koenig" operator that takes its left hand side by value. Oh, and don't return anything by rvalue reference, that is toxic.

friend path_basic_string
    operator +(
        path_basic_string l,
        base_type const& r)
{
  base_type& l_str = l;
  if (!r.empty())
    l = path_basic_string( std::move(l_str) + "/" + r );
  return l;
}

并消除所有杂音.

接下来,从base_type继承ctor.

Next, inherit ctors from base_type.

最后,使用+=实现附加,并使操作对称:

Finally, implement appending using += and make the operations symmetric:

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 & operator =(const path_basic_string &) = default;

    using base_type::base_type;

    explicit path_basic_string(base_type r) :
        base_type(std::move(r))
    {
    }
    path_basic_string& operator+= ( base_type const& rhs ) & {
      if (!rhs.empty())
      {
        base_type& self = *this;
        self += '/';
        self += rhs;
      }
      return *this;
    }
    friend path_basic_string operator+(
            base_type l,
            base_type const& r
    )
    {
      path_basic_string l_path(std::move(l));
      l+=r;
      return l;
    }
};

operator+在这里很花哨,因为只能通过ADL找到它,但实际上它是在类的 base 类型上运行的.

operator+ here is fancy as it is only findable via ADL, yet it actually operates on the base type of the class.

这意味着至少有一个参数必须是这种类型的实例(或具有这种类型的实例作为模板参数)才能被找到.

This means that at least one of the arguments must be an instance of this type (or have an instance of this type as a template argument) in order for it to be found.

然后根据需要进行基础转换.

Then conversion-to-base occurs if required.

我将LHS作为值,因为移动字符串很便宜,而且我们需要一个字符串才能输出.通过将LHS按值取值并将其缓冲区(移动它后)作为返回值,我们可以获得有效的链式加法:

I take LHS by value, because moving a string is cheap-to-free, and we need a string for output. By taking LHS by value and using its buffer (after moving it) for the return value, we get efficient chained addition:

a+b+c+d+e

成为

(a+b)+c+d+e

现在,将a+b的返回值(一个prvalue)用作(a+b)+c的lhs参数.

now the return value of a+b (a prvalue) is then used as the lhs argument of (a+b)+c.

此缓冲区的回收继续进行; (从第一个+开始)只创建一个缓冲区,然后将其移动,调整大小(希望有效)并在表达式的其余部分重新使用.

This recycling of the buffer continues; only one buffer is created (from the first +), and it is then moved, resized (hopefully efficiently) and reused for the rest of the expression.

这篇关于std :: string类继承和繁琐的c ++重载解析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆