0

前の質問: std::string クラスの継承と面倒な C++ オーバーロードの解決

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)演算子は正確に一致する必要がありますが、なぜ呼び出されないのでしょうか?

remove(3)の場合、 which is(1)の代わりに が呼び出され、(2)よりもよく一致します(1)

ここで何が起こっているのですか?

PS : これは、前の質問のconst+のような問題と同じだと思います。single reference

4

1 に答える 1