lundi 5 novembre 2018

std::string class inheritance and tedious c++ overload resolution

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;
}

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

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

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

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

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

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

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

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

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

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

Compiler returned: 2

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

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

: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);

                                    ^

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

 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; _Alloc = std::allocator]

 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?

Aucun commentaire:

Enregistrer un commentaire