<string>: basic_string<unicorn>::find_meow_of family with std::char_traits<unicorn> specialization are not supported · Issue #4930 · microsoft/STL (original) (raw)

Describe the bug

Discovered when trying to make follow up optimizations after #4744

basic_string with odd types and char_traits specialization in std:: can exist.
From [namespace.std]/2 :

Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace std provided that

However with std::char_traits<unicorn> specialization provided, basic_string<unicorn>::find_first_of fails to compile here:

static_assert(is_unsigned_v<_Elem>,
"Standard char_traits is only provided for char, wchar_t, char16_t, and char32_t. See N4950 [char.traits]. "
"Visual C++ accepts other unsigned integral types as an extension.");

Command-line test case

**********************************************************************
** Visual Studio 2022 Developer Command Prompt v17.12.0-pre.1.0
** Copyright (c) 2022 Microsoft Corporation
**********************************************************************
[vcvarsall.bat] Environment initialized for: 'x64'

C:\Program Files\Microsoft Visual Studio\2022\Preview>cd %temp%

C:\Users\ALEXG~1\AppData\Local\Temp>type repro.cpp

#include #include

static constexpr unsigned char odd_mask = 0xF;

enum odd_char : unsigned char {};

namespace std { template <> class char_traits { public: using char_type = odd_char; using int_type = int; using off_type = streamoff; using pos_type = streampos; using state_type = mbstate_t;

    static bool eq(const char_type c, const char_type d) {
        return ((static_cast<unsigned char>(c) ^ static_cast<unsigned char>(d)) & odd_mask) == 0;
    }

    static bool lt(const char_type c, const char_type d) {
        return (static_cast<unsigned char>(c) & odd_mask) < (static_cast<unsigned char>(d) & odd_mask);
    }

    static int compare(const char_type* const c, const char_type* const d, const size_t n) {
        for (size_t i = 0; i != n; ++i) {
            int ci = static_cast<unsigned char>(c[i]) & odd_mask;
            int di = static_cast<unsigned char>(d[i]) & odd_mask;
            int r  = ci - di;
            if (r != 0) {
                return r;
            }
        }

        return 0;
    }

    static size_t length(const char_type* const p) {
        const char_type* c = p;
        while (static_cast<unsigned char>(*c) != 0) {
            ++c;
        }

        return static_cast<size_t>(c - p);
    }

    static const char_type* find(const char_type* p, const size_t n, const char_type c) {
        for (size_t i = 0; i != n; ++i) {
            if (eq(p[i], c)) {
                return p + i;
            }
        }

        return nullptr;
    }

    static char_type* move(char_type* s, const char_type* p, const size_t n) {
        memmove(s, p, n);
        return s;
    }

    static char_type* copy(char_type* s, const char_type* p, const size_t n) {
        memmove(s, p, n);
        return s;
    }

    static void assign(char_type& r, const char_type& d) {
        r = d;
    }
    static char_type* assign(char_type* const s, const std::size_t n, const char_type c) {
        memset(s, static_cast<unsigned char>(c), n);
        return s;
    }

    static bool not_eof(int) {
        return true;
    }

    static char_type to_char_type(const int_type i) {
        return char_type(static_cast<unsigned char>(i));
    }

    static int_type to_int_type(const char_type i) {
        return static_cast<unsigned char>(i);
    }

    static bool eq_int_type(const int_type c, const int_type d) {
        if (c == -1) {
            return d == -1;
        } else {
            return ((c ^ d) & odd_mask) == 0;
        }
    }

    static int_type eof() {
        return -1;
    }
};

}; // namespace std

using odd_string = std::basic_string;

int main() { const odd_char s_init[] = {static_cast(0x55), static_cast(0x44), static_cast(0x33), static_cast(0x22), static_cast(0x11), static_cast(0)}; odd_string s(s_init);

assert(s.length() == 5);
assert(s.find(static_cast<odd_char>(0x54)) == 1);
assert(s.find(static_cast<odd_char>(0x26)) == s.npos);

const odd_char s2_init[] = {static_cast<odd_char>(0x83), static_cast<odd_char>(0x12), static_cast<odd_char>(0)};
odd_string s2(s2_init);

assert(s.find_first_of(s2) == 2);
assert(s2.find_first_of(s) == 0);

assert(s.find_last_of(s2) == 3);
assert(s2.find_last_of(s) == 1);

return 0;

}

C:\Users\ALEXG~1\AppData\Local\Temp>cl /Od /MDd repro.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.42.34226.3 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

repro.cpp
C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.42.34226\include\__msvc_string_view.hpp(664): error C2338: static_assert failed: 'Standard char_traits is only provided for char, wchar_t, char16_t, and char32_t. See N4950 [char.traits]. Visual C++ accepts other unsigned integral types as an extension.'
C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.42.34226\include\__msvc_string_view.hpp(664): note: the template instantiation context (the oldest one first) is
repro.cpp(107): note: see reference to class template instantiation 'std::basic_string<odd_char,std::char_traits<odd_char>,std::allocator<odd_char>>' being compiled
C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.42.34226\include\xstring(2693): note: while compiling class template member function 'unsigned __int64 std::basic_string<odd_char,std::char_traits<odd_char>,std::allocator<odd_char>>::find_last_of(const std::basic_string<odd_char,std::char_traits<odd_char>,std::allocator<odd_char>> &,unsigned __int64) noexcept const'
repro.cpp(119): note: see the first reference to 'std::basic_string<odd_char,std::char_traits<odd_char>,std::allocator<odd_char>>::find_last_of' in 'main'
C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.42.34226\include\xstring(2695): note: see reference to function template instantiation 'size_t std::_Traits_find_last_of<_Traits,true>(const odd_char *const ,const size_t,const size_t,const odd_char *const ,const size_t) noexcept' being compiled
        with
        [
            _Traits=std::char_traits<odd_char>
        ]
C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.42.34226\include\__msvc_string_view.hpp(730): note: see reference to class template instantiation 'std::_String_bitmap<std::char_traits<odd_char>::char_type,false>' being compiled

Expected behavior

Should compile

STL version

Version 17.12.0 Preview 1.0