Using template to handle string and wstring












1














I have following two functions:



void bar(const std::string &s)
{
someCFunctionU(s.c_str());
}

void bar(const std::wstring &s)
{
someCFunctionW(s.c_str());
}


Both of these call some C function which accepts const char * or const wchar_t * and have U or W suffixes respectively. I would like to create a template function to handle both of these cases. I tried following attempt:



template <typename T>
void foo(const std::basic_string<T> &s)
{
if constexpr (std::is_same_v<T, char>)
someCFunctionU(s.c_str());
else
someCFunctionW(s.c_str());
}


But this does not seem to work correctly. If I call:



foo("abc");


this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char? Is it possible to create one function which would handle both std::string and std::wstring?










share|improve this question






















  • "abc" is const char* and not a std::(w)string
    – MrTux
    Nov 20 at 18:55






  • 2




    It's actually a const char[N], but it will decay to a const char*
    – NathanOliver
    Nov 20 at 18:55










  • @NathanOliver Why would it decay? During deduction, the parameter is a reference. And during constructor call, it will use the the overload which takes std::initializer_list<char>.
    – felix
    Nov 20 at 19:24












  • @felix I didn't mean during deduction. I just mean generally it will decay to a char*. I should have been more specific.
    – NathanOliver
    Nov 20 at 19:25












  • @NathanOliver Yes, sometimes. But not in this context. I don't know which part you mean. Problem is that such decay won't happen here in any part.
    – felix
    Nov 20 at 19:26


















1














I have following two functions:



void bar(const std::string &s)
{
someCFunctionU(s.c_str());
}

void bar(const std::wstring &s)
{
someCFunctionW(s.c_str());
}


Both of these call some C function which accepts const char * or const wchar_t * and have U or W suffixes respectively. I would like to create a template function to handle both of these cases. I tried following attempt:



template <typename T>
void foo(const std::basic_string<T> &s)
{
if constexpr (std::is_same_v<T, char>)
someCFunctionU(s.c_str());
else
someCFunctionW(s.c_str());
}


But this does not seem to work correctly. If I call:



foo("abc");


this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char? Is it possible to create one function which would handle both std::string and std::wstring?










share|improve this question






















  • "abc" is const char* and not a std::(w)string
    – MrTux
    Nov 20 at 18:55






  • 2




    It's actually a const char[N], but it will decay to a const char*
    – NathanOliver
    Nov 20 at 18:55










  • @NathanOliver Why would it decay? During deduction, the parameter is a reference. And during constructor call, it will use the the overload which takes std::initializer_list<char>.
    – felix
    Nov 20 at 19:24












  • @felix I didn't mean during deduction. I just mean generally it will decay to a char*. I should have been more specific.
    – NathanOliver
    Nov 20 at 19:25












  • @NathanOliver Yes, sometimes. But not in this context. I don't know which part you mean. Problem is that such decay won't happen here in any part.
    – felix
    Nov 20 at 19:26
















1












1








1







I have following two functions:



void bar(const std::string &s)
{
someCFunctionU(s.c_str());
}

void bar(const std::wstring &s)
{
someCFunctionW(s.c_str());
}


Both of these call some C function which accepts const char * or const wchar_t * and have U or W suffixes respectively. I would like to create a template function to handle both of these cases. I tried following attempt:



template <typename T>
void foo(const std::basic_string<T> &s)
{
if constexpr (std::is_same_v<T, char>)
someCFunctionU(s.c_str());
else
someCFunctionW(s.c_str());
}


But this does not seem to work correctly. If I call:



foo("abc");


this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char? Is it possible to create one function which would handle both std::string and std::wstring?










share|improve this question













I have following two functions:



void bar(const std::string &s)
{
someCFunctionU(s.c_str());
}

void bar(const std::wstring &s)
{
someCFunctionW(s.c_str());
}


Both of these call some C function which accepts const char * or const wchar_t * and have U or W suffixes respectively. I would like to create a template function to handle both of these cases. I tried following attempt:



template <typename T>
void foo(const std::basic_string<T> &s)
{
if constexpr (std::is_same_v<T, char>)
someCFunctionU(s.c_str());
else
someCFunctionW(s.c_str());
}


But this does not seem to work correctly. If I call:



foo("abc");


this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char? Is it possible to create one function which would handle both std::string and std::wstring?







c++ templates c++17 template-meta-programming






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 20 at 18:53









Igor

567411




567411












  • "abc" is const char* and not a std::(w)string
    – MrTux
    Nov 20 at 18:55






  • 2




    It's actually a const char[N], but it will decay to a const char*
    – NathanOliver
    Nov 20 at 18:55










  • @NathanOliver Why would it decay? During deduction, the parameter is a reference. And during constructor call, it will use the the overload which takes std::initializer_list<char>.
    – felix
    Nov 20 at 19:24












  • @felix I didn't mean during deduction. I just mean generally it will decay to a char*. I should have been more specific.
    – NathanOliver
    Nov 20 at 19:25












  • @NathanOliver Yes, sometimes. But not in this context. I don't know which part you mean. Problem is that such decay won't happen here in any part.
    – felix
    Nov 20 at 19:26




















  • "abc" is const char* and not a std::(w)string
    – MrTux
    Nov 20 at 18:55






  • 2




    It's actually a const char[N], but it will decay to a const char*
    – NathanOliver
    Nov 20 at 18:55










  • @NathanOliver Why would it decay? During deduction, the parameter is a reference. And during constructor call, it will use the the overload which takes std::initializer_list<char>.
    – felix
    Nov 20 at 19:24












  • @felix I didn't mean during deduction. I just mean generally it will decay to a char*. I should have been more specific.
    – NathanOliver
    Nov 20 at 19:25












  • @NathanOliver Yes, sometimes. But not in this context. I don't know which part you mean. Problem is that such decay won't happen here in any part.
    – felix
    Nov 20 at 19:26


















"abc" is const char* and not a std::(w)string
– MrTux
Nov 20 at 18:55




"abc" is const char* and not a std::(w)string
– MrTux
Nov 20 at 18:55




2




2




It's actually a const char[N], but it will decay to a const char*
– NathanOliver
Nov 20 at 18:55




It's actually a const char[N], but it will decay to a const char*
– NathanOliver
Nov 20 at 18:55












@NathanOliver Why would it decay? During deduction, the parameter is a reference. And during constructor call, it will use the the overload which takes std::initializer_list<char>.
– felix
Nov 20 at 19:24






@NathanOliver Why would it decay? During deduction, the parameter is a reference. And during constructor call, it will use the the overload which takes std::initializer_list<char>.
– felix
Nov 20 at 19:24














@felix I didn't mean during deduction. I just mean generally it will decay to a char*. I should have been more specific.
– NathanOliver
Nov 20 at 19:25






@felix I didn't mean during deduction. I just mean generally it will decay to a char*. I should have been more specific.
– NathanOliver
Nov 20 at 19:25














@NathanOliver Yes, sometimes. But not in this context. I don't know which part you mean. Problem is that such decay won't happen here in any part.
– felix
Nov 20 at 19:26






@NathanOliver Yes, sometimes. But not in this context. I don't know which part you mean. Problem is that such decay won't happen here in any part.
– felix
Nov 20 at 19:26














4 Answers
4






active

oldest

votes


















4















this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char?




As better explained by others, "abc" is a char[4], so is convertible to a std::basic_string<char> but isn't a std::basic_string<char>, so can't be deduced the T type as char for a template function that accept a std::basic_string<T>.




Is it possible to create one function which would handle both std::string and std::wstring?




Yes, it's possible; but what's wrong with your two-function-in-overloading solution?



Anyway, if you really want a single function and if you accept to write a lot of casuistry, I suppose you can write something as follows



template <typename T>
void foo (T const & s)
{
if constexpr ( std::is_same_v<T, std::string> )
someCFunctionU(s.c_str());
else if constexpr ( std::is_convertible_v<T, char const *>)
someCFunctionU(s);
else if constexpr ( std::is_same_v<T, std::wstring> )
someCFunctionW(s.c_str());
else if constexpr ( std::is_convertible_v<T, wchar_t const *> )
someCFunctionW(s);
// else exception ?
}


or, a little more synthetic but less efficient



template <typename T>
void foo (T const & s)
{
if constexpr ( std::is_convertible_v<T, std::string> )
someCFunctionU(std::string{s}.c_str());
else if constexpr (std::is_convertible_v<T, std::wstring> )
someCFunctionW(std::wstring{s}.c_str());
// else exception ?
}


So you should be able to call foo() with std::string, std::wstring, char *, wchar_t *, char or wchar_t.






share|improve this answer































    4














    The issue here is that in foo("abc");, "abc" is not a std::string or a std::wstring, it is a const char[N]. Since it isn't a std::string or a std::wstring the compiler cannot deduce what T should be and it fails to compile. The easiest solution is to use what you already have. The overloads will be considered and it is a better match to convert "abc" to a std::string so it will call that version of the function.



    If you want you could use a std::string_view/std::wstring_view instead of std::string/std::wstring so you don't actually allocate any memory if you pass the function a string literal. That would change the overloads to



    void bar(std::string_view s)
    {
    someCFunctionU(s.data());
    }

    void bar(std::wstring_view s)
    {
    someCFunctionW(s.data());
    }


    Do note that std::basic_string_view can be constructed without having a null terminator so it is possible to pass a std::basic_string_view that won't fulfill the null terminated c-string requirement that your C function has. In that case the code has undefined behavior.






    share|improve this answer























    • Hmm. string_view can't guarantee a null-terminated string, so passing s.data() to the C-style function is dangerous and should probably be avoided, since that C-style function expects a null-terminated string. If the C-style function could be changed to take a pointer and a length, all would be fine. Alternatively, there may be some non-standard string_view implementations which guarantee null-termination, meaning that they can only be at the end of a string
      – Justin
      Nov 20 at 19:11






    • 1




      @Justin That really upsets me with string_view that we can't do this. They give me this nice shinny thing but then rip the carpet out from under my feet. I'll put a warning in the answer for the OP, just so they know, but it should be okay if the only feed strings/c-strings to the function.
      – NathanOliver
      Nov 20 at 19:15










    • string_view with guaranteed null-termination is not as useful. It means that the string_view can only point to the end of strings, rather than any substring.
      – Justin
      Nov 20 at 19:16








    • 1




      @Justin That's why I really wanted a slice class. I always envisioned a string_view as a read only view of a string with all the member functions. A slice or string_slice would have the non-guarantees.
      – NathanOliver
      Nov 20 at 19:19



















    2














    A workaround in C++17 is:



    template <typename T>
    void foo(const T &s)
    {
    std::basic_string_view sv{s}; // Class template argument deduction

    if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
    someCFunctionU(sv.data());
    else
    someCFunctionW(sv.data());
    }


    And to avoid issue mentioned by Justin about non-null-terminated string



    template <typename T> struct is_basic_string_view : std::false_type {};

    template <typename T> struct is_basic_string_view<basic_string_view<T>> : std::true_type
    {};

    template <typename T>
    std::enable_if_t<!is_basic_string_view<T>::value> foo(const T &s)
    {
    std::basic_string_view sv{s}; // Class template argument deduction

    if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
    someCFunctionU(sv.data());
    else
    someCFunctionW(sv.data());
    }





    share|improve this answer























    • This has the same danger I mentioned in NathanOliver's answer: if you pass a non-null-terminated string_view to foo(...), this will accept it, passing a non-null-terminated string to the C functions, which is likely to cause problems.
      – Justin
      Nov 20 at 19:15






    • 1




      @Justin: Safer version added.
      – Jarod42
      Nov 20 at 19:21



















    1














    Yes, there exist a type, i.e. std::basic_string<char>, which can be copy initialized from expression "abc". So you can call a function like void foo(std::basic_string<char>) with argument "abc".



    And no, you can't call a function template template <class T> void foo(const std::basic_string<T> &s) with argument "abc". Because in order to figure out whether the parameter can be initialized by the argument, the compiler need to determine the template parameter T first. It will try to match const std::basic_string<T> & against const char [4]. And it will fail.



    The reason why it will fail is because of the template argument deduction rule. The actual rule is very complicated. But in this case, for std::basic_string<char> to be examined during the deduction, compiler will need to look for a proper "converting constructor", i.e. the constructor which can be called implicitly with argument "abc", and such lookup isn't allowed by the standard during deduction.



    Yes, it is possible to handle std::string and std::wstring in one function template:



    void foo_impl(const std::string &) {}
    void foo_impl(const std::wstring &) {}

    template <class T>
    auto foo(T &&t) {
    return foo_impl(std::forward<T>(t));
    }





    share|improve this answer























    • std::basic_string<char> is exactly the same as std::string
      – Justin
      Nov 20 at 19:08










    • @Justin Yes, std::string is its alias. But the original question is using std::basic_string. And in this context, using std::string is inconvenient.
      – felix
      Nov 20 at 19:15











    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53399688%2fusing-template-to-handle-string-and-wstring%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    4 Answers
    4






    active

    oldest

    votes








    4 Answers
    4






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    4















    this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char?




    As better explained by others, "abc" is a char[4], so is convertible to a std::basic_string<char> but isn't a std::basic_string<char>, so can't be deduced the T type as char for a template function that accept a std::basic_string<T>.




    Is it possible to create one function which would handle both std::string and std::wstring?




    Yes, it's possible; but what's wrong with your two-function-in-overloading solution?



    Anyway, if you really want a single function and if you accept to write a lot of casuistry, I suppose you can write something as follows



    template <typename T>
    void foo (T const & s)
    {
    if constexpr ( std::is_same_v<T, std::string> )
    someCFunctionU(s.c_str());
    else if constexpr ( std::is_convertible_v<T, char const *>)
    someCFunctionU(s);
    else if constexpr ( std::is_same_v<T, std::wstring> )
    someCFunctionW(s.c_str());
    else if constexpr ( std::is_convertible_v<T, wchar_t const *> )
    someCFunctionW(s);
    // else exception ?
    }


    or, a little more synthetic but less efficient



    template <typename T>
    void foo (T const & s)
    {
    if constexpr ( std::is_convertible_v<T, std::string> )
    someCFunctionU(std::string{s}.c_str());
    else if constexpr (std::is_convertible_v<T, std::wstring> )
    someCFunctionW(std::wstring{s}.c_str());
    // else exception ?
    }


    So you should be able to call foo() with std::string, std::wstring, char *, wchar_t *, char or wchar_t.






    share|improve this answer




























      4















      this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char?




      As better explained by others, "abc" is a char[4], so is convertible to a std::basic_string<char> but isn't a std::basic_string<char>, so can't be deduced the T type as char for a template function that accept a std::basic_string<T>.




      Is it possible to create one function which would handle both std::string and std::wstring?




      Yes, it's possible; but what's wrong with your two-function-in-overloading solution?



      Anyway, if you really want a single function and if you accept to write a lot of casuistry, I suppose you can write something as follows



      template <typename T>
      void foo (T const & s)
      {
      if constexpr ( std::is_same_v<T, std::string> )
      someCFunctionU(s.c_str());
      else if constexpr ( std::is_convertible_v<T, char const *>)
      someCFunctionU(s);
      else if constexpr ( std::is_same_v<T, std::wstring> )
      someCFunctionW(s.c_str());
      else if constexpr ( std::is_convertible_v<T, wchar_t const *> )
      someCFunctionW(s);
      // else exception ?
      }


      or, a little more synthetic but less efficient



      template <typename T>
      void foo (T const & s)
      {
      if constexpr ( std::is_convertible_v<T, std::string> )
      someCFunctionU(std::string{s}.c_str());
      else if constexpr (std::is_convertible_v<T, std::wstring> )
      someCFunctionW(std::wstring{s}.c_str());
      // else exception ?
      }


      So you should be able to call foo() with std::string, std::wstring, char *, wchar_t *, char or wchar_t.






      share|improve this answer


























        4












        4








        4







        this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char?




        As better explained by others, "abc" is a char[4], so is convertible to a std::basic_string<char> but isn't a std::basic_string<char>, so can't be deduced the T type as char for a template function that accept a std::basic_string<T>.




        Is it possible to create one function which would handle both std::string and std::wstring?




        Yes, it's possible; but what's wrong with your two-function-in-overloading solution?



        Anyway, if you really want a single function and if you accept to write a lot of casuistry, I suppose you can write something as follows



        template <typename T>
        void foo (T const & s)
        {
        if constexpr ( std::is_same_v<T, std::string> )
        someCFunctionU(s.c_str());
        else if constexpr ( std::is_convertible_v<T, char const *>)
        someCFunctionU(s);
        else if constexpr ( std::is_same_v<T, std::wstring> )
        someCFunctionW(s.c_str());
        else if constexpr ( std::is_convertible_v<T, wchar_t const *> )
        someCFunctionW(s);
        // else exception ?
        }


        or, a little more synthetic but less efficient



        template <typename T>
        void foo (T const & s)
        {
        if constexpr ( std::is_convertible_v<T, std::string> )
        someCFunctionU(std::string{s}.c_str());
        else if constexpr (std::is_convertible_v<T, std::wstring> )
        someCFunctionW(std::wstring{s}.c_str());
        // else exception ?
        }


        So you should be able to call foo() with std::string, std::wstring, char *, wchar_t *, char or wchar_t.






        share|improve this answer















        this will not compile. Why is that? why a compiler is not able to deduce the proper type T to char?




        As better explained by others, "abc" is a char[4], so is convertible to a std::basic_string<char> but isn't a std::basic_string<char>, so can't be deduced the T type as char for a template function that accept a std::basic_string<T>.




        Is it possible to create one function which would handle both std::string and std::wstring?




        Yes, it's possible; but what's wrong with your two-function-in-overloading solution?



        Anyway, if you really want a single function and if you accept to write a lot of casuistry, I suppose you can write something as follows



        template <typename T>
        void foo (T const & s)
        {
        if constexpr ( std::is_same_v<T, std::string> )
        someCFunctionU(s.c_str());
        else if constexpr ( std::is_convertible_v<T, char const *>)
        someCFunctionU(s);
        else if constexpr ( std::is_same_v<T, std::wstring> )
        someCFunctionW(s.c_str());
        else if constexpr ( std::is_convertible_v<T, wchar_t const *> )
        someCFunctionW(s);
        // else exception ?
        }


        or, a little more synthetic but less efficient



        template <typename T>
        void foo (T const & s)
        {
        if constexpr ( std::is_convertible_v<T, std::string> )
        someCFunctionU(std::string{s}.c_str());
        else if constexpr (std::is_convertible_v<T, std::wstring> )
        someCFunctionW(std::wstring{s}.c_str());
        // else exception ?
        }


        So you should be able to call foo() with std::string, std::wstring, char *, wchar_t *, char or wchar_t.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Nov 20 at 19:33

























        answered Nov 20 at 19:27









        max66

        34.2k63762




        34.2k63762

























            4














            The issue here is that in foo("abc");, "abc" is not a std::string or a std::wstring, it is a const char[N]. Since it isn't a std::string or a std::wstring the compiler cannot deduce what T should be and it fails to compile. The easiest solution is to use what you already have. The overloads will be considered and it is a better match to convert "abc" to a std::string so it will call that version of the function.



            If you want you could use a std::string_view/std::wstring_view instead of std::string/std::wstring so you don't actually allocate any memory if you pass the function a string literal. That would change the overloads to



            void bar(std::string_view s)
            {
            someCFunctionU(s.data());
            }

            void bar(std::wstring_view s)
            {
            someCFunctionW(s.data());
            }


            Do note that std::basic_string_view can be constructed without having a null terminator so it is possible to pass a std::basic_string_view that won't fulfill the null terminated c-string requirement that your C function has. In that case the code has undefined behavior.






            share|improve this answer























            • Hmm. string_view can't guarantee a null-terminated string, so passing s.data() to the C-style function is dangerous and should probably be avoided, since that C-style function expects a null-terminated string. If the C-style function could be changed to take a pointer and a length, all would be fine. Alternatively, there may be some non-standard string_view implementations which guarantee null-termination, meaning that they can only be at the end of a string
              – Justin
              Nov 20 at 19:11






            • 1




              @Justin That really upsets me with string_view that we can't do this. They give me this nice shinny thing but then rip the carpet out from under my feet. I'll put a warning in the answer for the OP, just so they know, but it should be okay if the only feed strings/c-strings to the function.
              – NathanOliver
              Nov 20 at 19:15










            • string_view with guaranteed null-termination is not as useful. It means that the string_view can only point to the end of strings, rather than any substring.
              – Justin
              Nov 20 at 19:16








            • 1




              @Justin That's why I really wanted a slice class. I always envisioned a string_view as a read only view of a string with all the member functions. A slice or string_slice would have the non-guarantees.
              – NathanOliver
              Nov 20 at 19:19
















            4














            The issue here is that in foo("abc");, "abc" is not a std::string or a std::wstring, it is a const char[N]. Since it isn't a std::string or a std::wstring the compiler cannot deduce what T should be and it fails to compile. The easiest solution is to use what you already have. The overloads will be considered and it is a better match to convert "abc" to a std::string so it will call that version of the function.



            If you want you could use a std::string_view/std::wstring_view instead of std::string/std::wstring so you don't actually allocate any memory if you pass the function a string literal. That would change the overloads to



            void bar(std::string_view s)
            {
            someCFunctionU(s.data());
            }

            void bar(std::wstring_view s)
            {
            someCFunctionW(s.data());
            }


            Do note that std::basic_string_view can be constructed without having a null terminator so it is possible to pass a std::basic_string_view that won't fulfill the null terminated c-string requirement that your C function has. In that case the code has undefined behavior.






            share|improve this answer























            • Hmm. string_view can't guarantee a null-terminated string, so passing s.data() to the C-style function is dangerous and should probably be avoided, since that C-style function expects a null-terminated string. If the C-style function could be changed to take a pointer and a length, all would be fine. Alternatively, there may be some non-standard string_view implementations which guarantee null-termination, meaning that they can only be at the end of a string
              – Justin
              Nov 20 at 19:11






            • 1




              @Justin That really upsets me with string_view that we can't do this. They give me this nice shinny thing but then rip the carpet out from under my feet. I'll put a warning in the answer for the OP, just so they know, but it should be okay if the only feed strings/c-strings to the function.
              – NathanOliver
              Nov 20 at 19:15










            • string_view with guaranteed null-termination is not as useful. It means that the string_view can only point to the end of strings, rather than any substring.
              – Justin
              Nov 20 at 19:16








            • 1




              @Justin That's why I really wanted a slice class. I always envisioned a string_view as a read only view of a string with all the member functions. A slice or string_slice would have the non-guarantees.
              – NathanOliver
              Nov 20 at 19:19














            4












            4








            4






            The issue here is that in foo("abc");, "abc" is not a std::string or a std::wstring, it is a const char[N]. Since it isn't a std::string or a std::wstring the compiler cannot deduce what T should be and it fails to compile. The easiest solution is to use what you already have. The overloads will be considered and it is a better match to convert "abc" to a std::string so it will call that version of the function.



            If you want you could use a std::string_view/std::wstring_view instead of std::string/std::wstring so you don't actually allocate any memory if you pass the function a string literal. That would change the overloads to



            void bar(std::string_view s)
            {
            someCFunctionU(s.data());
            }

            void bar(std::wstring_view s)
            {
            someCFunctionW(s.data());
            }


            Do note that std::basic_string_view can be constructed without having a null terminator so it is possible to pass a std::basic_string_view that won't fulfill the null terminated c-string requirement that your C function has. In that case the code has undefined behavior.






            share|improve this answer














            The issue here is that in foo("abc");, "abc" is not a std::string or a std::wstring, it is a const char[N]. Since it isn't a std::string or a std::wstring the compiler cannot deduce what T should be and it fails to compile. The easiest solution is to use what you already have. The overloads will be considered and it is a better match to convert "abc" to a std::string so it will call that version of the function.



            If you want you could use a std::string_view/std::wstring_view instead of std::string/std::wstring so you don't actually allocate any memory if you pass the function a string literal. That would change the overloads to



            void bar(std::string_view s)
            {
            someCFunctionU(s.data());
            }

            void bar(std::wstring_view s)
            {
            someCFunctionW(s.data());
            }


            Do note that std::basic_string_view can be constructed without having a null terminator so it is possible to pass a std::basic_string_view that won't fulfill the null terminated c-string requirement that your C function has. In that case the code has undefined behavior.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 20 at 19:17

























            answered Nov 20 at 19:02









            NathanOliver

            86.6k15120180




            86.6k15120180












            • Hmm. string_view can't guarantee a null-terminated string, so passing s.data() to the C-style function is dangerous and should probably be avoided, since that C-style function expects a null-terminated string. If the C-style function could be changed to take a pointer and a length, all would be fine. Alternatively, there may be some non-standard string_view implementations which guarantee null-termination, meaning that they can only be at the end of a string
              – Justin
              Nov 20 at 19:11






            • 1




              @Justin That really upsets me with string_view that we can't do this. They give me this nice shinny thing but then rip the carpet out from under my feet. I'll put a warning in the answer for the OP, just so they know, but it should be okay if the only feed strings/c-strings to the function.
              – NathanOliver
              Nov 20 at 19:15










            • string_view with guaranteed null-termination is not as useful. It means that the string_view can only point to the end of strings, rather than any substring.
              – Justin
              Nov 20 at 19:16








            • 1




              @Justin That's why I really wanted a slice class. I always envisioned a string_view as a read only view of a string with all the member functions. A slice or string_slice would have the non-guarantees.
              – NathanOliver
              Nov 20 at 19:19


















            • Hmm. string_view can't guarantee a null-terminated string, so passing s.data() to the C-style function is dangerous and should probably be avoided, since that C-style function expects a null-terminated string. If the C-style function could be changed to take a pointer and a length, all would be fine. Alternatively, there may be some non-standard string_view implementations which guarantee null-termination, meaning that they can only be at the end of a string
              – Justin
              Nov 20 at 19:11






            • 1




              @Justin That really upsets me with string_view that we can't do this. They give me this nice shinny thing but then rip the carpet out from under my feet. I'll put a warning in the answer for the OP, just so they know, but it should be okay if the only feed strings/c-strings to the function.
              – NathanOliver
              Nov 20 at 19:15










            • string_view with guaranteed null-termination is not as useful. It means that the string_view can only point to the end of strings, rather than any substring.
              – Justin
              Nov 20 at 19:16








            • 1




              @Justin That's why I really wanted a slice class. I always envisioned a string_view as a read only view of a string with all the member functions. A slice or string_slice would have the non-guarantees.
              – NathanOliver
              Nov 20 at 19:19
















            Hmm. string_view can't guarantee a null-terminated string, so passing s.data() to the C-style function is dangerous and should probably be avoided, since that C-style function expects a null-terminated string. If the C-style function could be changed to take a pointer and a length, all would be fine. Alternatively, there may be some non-standard string_view implementations which guarantee null-termination, meaning that they can only be at the end of a string
            – Justin
            Nov 20 at 19:11




            Hmm. string_view can't guarantee a null-terminated string, so passing s.data() to the C-style function is dangerous and should probably be avoided, since that C-style function expects a null-terminated string. If the C-style function could be changed to take a pointer and a length, all would be fine. Alternatively, there may be some non-standard string_view implementations which guarantee null-termination, meaning that they can only be at the end of a string
            – Justin
            Nov 20 at 19:11




            1




            1




            @Justin That really upsets me with string_view that we can't do this. They give me this nice shinny thing but then rip the carpet out from under my feet. I'll put a warning in the answer for the OP, just so they know, but it should be okay if the only feed strings/c-strings to the function.
            – NathanOliver
            Nov 20 at 19:15




            @Justin That really upsets me with string_view that we can't do this. They give me this nice shinny thing but then rip the carpet out from under my feet. I'll put a warning in the answer for the OP, just so they know, but it should be okay if the only feed strings/c-strings to the function.
            – NathanOliver
            Nov 20 at 19:15












            string_view with guaranteed null-termination is not as useful. It means that the string_view can only point to the end of strings, rather than any substring.
            – Justin
            Nov 20 at 19:16






            string_view with guaranteed null-termination is not as useful. It means that the string_view can only point to the end of strings, rather than any substring.
            – Justin
            Nov 20 at 19:16






            1




            1




            @Justin That's why I really wanted a slice class. I always envisioned a string_view as a read only view of a string with all the member functions. A slice or string_slice would have the non-guarantees.
            – NathanOliver
            Nov 20 at 19:19




            @Justin That's why I really wanted a slice class. I always envisioned a string_view as a read only view of a string with all the member functions. A slice or string_slice would have the non-guarantees.
            – NathanOliver
            Nov 20 at 19:19











            2














            A workaround in C++17 is:



            template <typename T>
            void foo(const T &s)
            {
            std::basic_string_view sv{s}; // Class template argument deduction

            if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
            someCFunctionU(sv.data());
            else
            someCFunctionW(sv.data());
            }


            And to avoid issue mentioned by Justin about non-null-terminated string



            template <typename T> struct is_basic_string_view : std::false_type {};

            template <typename T> struct is_basic_string_view<basic_string_view<T>> : std::true_type
            {};

            template <typename T>
            std::enable_if_t<!is_basic_string_view<T>::value> foo(const T &s)
            {
            std::basic_string_view sv{s}; // Class template argument deduction

            if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
            someCFunctionU(sv.data());
            else
            someCFunctionW(sv.data());
            }





            share|improve this answer























            • This has the same danger I mentioned in NathanOliver's answer: if you pass a non-null-terminated string_view to foo(...), this will accept it, passing a non-null-terminated string to the C functions, which is likely to cause problems.
              – Justin
              Nov 20 at 19:15






            • 1




              @Justin: Safer version added.
              – Jarod42
              Nov 20 at 19:21
















            2














            A workaround in C++17 is:



            template <typename T>
            void foo(const T &s)
            {
            std::basic_string_view sv{s}; // Class template argument deduction

            if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
            someCFunctionU(sv.data());
            else
            someCFunctionW(sv.data());
            }


            And to avoid issue mentioned by Justin about non-null-terminated string



            template <typename T> struct is_basic_string_view : std::false_type {};

            template <typename T> struct is_basic_string_view<basic_string_view<T>> : std::true_type
            {};

            template <typename T>
            std::enable_if_t<!is_basic_string_view<T>::value> foo(const T &s)
            {
            std::basic_string_view sv{s}; // Class template argument deduction

            if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
            someCFunctionU(sv.data());
            else
            someCFunctionW(sv.data());
            }





            share|improve this answer























            • This has the same danger I mentioned in NathanOliver's answer: if you pass a non-null-terminated string_view to foo(...), this will accept it, passing a non-null-terminated string to the C functions, which is likely to cause problems.
              – Justin
              Nov 20 at 19:15






            • 1




              @Justin: Safer version added.
              – Jarod42
              Nov 20 at 19:21














            2












            2








            2






            A workaround in C++17 is:



            template <typename T>
            void foo(const T &s)
            {
            std::basic_string_view sv{s}; // Class template argument deduction

            if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
            someCFunctionU(sv.data());
            else
            someCFunctionW(sv.data());
            }


            And to avoid issue mentioned by Justin about non-null-terminated string



            template <typename T> struct is_basic_string_view : std::false_type {};

            template <typename T> struct is_basic_string_view<basic_string_view<T>> : std::true_type
            {};

            template <typename T>
            std::enable_if_t<!is_basic_string_view<T>::value> foo(const T &s)
            {
            std::basic_string_view sv{s}; // Class template argument deduction

            if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
            someCFunctionU(sv.data());
            else
            someCFunctionW(sv.data());
            }





            share|improve this answer














            A workaround in C++17 is:



            template <typename T>
            void foo(const T &s)
            {
            std::basic_string_view sv{s}; // Class template argument deduction

            if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
            someCFunctionU(sv.data());
            else
            someCFunctionW(sv.data());
            }


            And to avoid issue mentioned by Justin about non-null-terminated string



            template <typename T> struct is_basic_string_view : std::false_type {};

            template <typename T> struct is_basic_string_view<basic_string_view<T>> : std::true_type
            {};

            template <typename T>
            std::enable_if_t<!is_basic_string_view<T>::value> foo(const T &s)
            {
            std::basic_string_view sv{s}; // Class template argument deduction

            if constexpr (std::is_same_v<typename decltype(sv)::value_type, char>)
            someCFunctionU(sv.data());
            else
            someCFunctionW(sv.data());
            }






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 20 at 19:21

























            answered Nov 20 at 19:13









            Jarod42

            112k1299179




            112k1299179












            • This has the same danger I mentioned in NathanOliver's answer: if you pass a non-null-terminated string_view to foo(...), this will accept it, passing a non-null-terminated string to the C functions, which is likely to cause problems.
              – Justin
              Nov 20 at 19:15






            • 1




              @Justin: Safer version added.
              – Jarod42
              Nov 20 at 19:21


















            • This has the same danger I mentioned in NathanOliver's answer: if you pass a non-null-terminated string_view to foo(...), this will accept it, passing a non-null-terminated string to the C functions, which is likely to cause problems.
              – Justin
              Nov 20 at 19:15






            • 1




              @Justin: Safer version added.
              – Jarod42
              Nov 20 at 19:21
















            This has the same danger I mentioned in NathanOliver's answer: if you pass a non-null-terminated string_view to foo(...), this will accept it, passing a non-null-terminated string to the C functions, which is likely to cause problems.
            – Justin
            Nov 20 at 19:15




            This has the same danger I mentioned in NathanOliver's answer: if you pass a non-null-terminated string_view to foo(...), this will accept it, passing a non-null-terminated string to the C functions, which is likely to cause problems.
            – Justin
            Nov 20 at 19:15




            1




            1




            @Justin: Safer version added.
            – Jarod42
            Nov 20 at 19:21




            @Justin: Safer version added.
            – Jarod42
            Nov 20 at 19:21











            1














            Yes, there exist a type, i.e. std::basic_string<char>, which can be copy initialized from expression "abc". So you can call a function like void foo(std::basic_string<char>) with argument "abc".



            And no, you can't call a function template template <class T> void foo(const std::basic_string<T> &s) with argument "abc". Because in order to figure out whether the parameter can be initialized by the argument, the compiler need to determine the template parameter T first. It will try to match const std::basic_string<T> & against const char [4]. And it will fail.



            The reason why it will fail is because of the template argument deduction rule. The actual rule is very complicated. But in this case, for std::basic_string<char> to be examined during the deduction, compiler will need to look for a proper "converting constructor", i.e. the constructor which can be called implicitly with argument "abc", and such lookup isn't allowed by the standard during deduction.



            Yes, it is possible to handle std::string and std::wstring in one function template:



            void foo_impl(const std::string &) {}
            void foo_impl(const std::wstring &) {}

            template <class T>
            auto foo(T &&t) {
            return foo_impl(std::forward<T>(t));
            }





            share|improve this answer























            • std::basic_string<char> is exactly the same as std::string
              – Justin
              Nov 20 at 19:08










            • @Justin Yes, std::string is its alias. But the original question is using std::basic_string. And in this context, using std::string is inconvenient.
              – felix
              Nov 20 at 19:15
















            1














            Yes, there exist a type, i.e. std::basic_string<char>, which can be copy initialized from expression "abc". So you can call a function like void foo(std::basic_string<char>) with argument "abc".



            And no, you can't call a function template template <class T> void foo(const std::basic_string<T> &s) with argument "abc". Because in order to figure out whether the parameter can be initialized by the argument, the compiler need to determine the template parameter T first. It will try to match const std::basic_string<T> & against const char [4]. And it will fail.



            The reason why it will fail is because of the template argument deduction rule. The actual rule is very complicated. But in this case, for std::basic_string<char> to be examined during the deduction, compiler will need to look for a proper "converting constructor", i.e. the constructor which can be called implicitly with argument "abc", and such lookup isn't allowed by the standard during deduction.



            Yes, it is possible to handle std::string and std::wstring in one function template:



            void foo_impl(const std::string &) {}
            void foo_impl(const std::wstring &) {}

            template <class T>
            auto foo(T &&t) {
            return foo_impl(std::forward<T>(t));
            }





            share|improve this answer























            • std::basic_string<char> is exactly the same as std::string
              – Justin
              Nov 20 at 19:08










            • @Justin Yes, std::string is its alias. But the original question is using std::basic_string. And in this context, using std::string is inconvenient.
              – felix
              Nov 20 at 19:15














            1












            1








            1






            Yes, there exist a type, i.e. std::basic_string<char>, which can be copy initialized from expression "abc". So you can call a function like void foo(std::basic_string<char>) with argument "abc".



            And no, you can't call a function template template <class T> void foo(const std::basic_string<T> &s) with argument "abc". Because in order to figure out whether the parameter can be initialized by the argument, the compiler need to determine the template parameter T first. It will try to match const std::basic_string<T> & against const char [4]. And it will fail.



            The reason why it will fail is because of the template argument deduction rule. The actual rule is very complicated. But in this case, for std::basic_string<char> to be examined during the deduction, compiler will need to look for a proper "converting constructor", i.e. the constructor which can be called implicitly with argument "abc", and such lookup isn't allowed by the standard during deduction.



            Yes, it is possible to handle std::string and std::wstring in one function template:



            void foo_impl(const std::string &) {}
            void foo_impl(const std::wstring &) {}

            template <class T>
            auto foo(T &&t) {
            return foo_impl(std::forward<T>(t));
            }





            share|improve this answer














            Yes, there exist a type, i.e. std::basic_string<char>, which can be copy initialized from expression "abc". So you can call a function like void foo(std::basic_string<char>) with argument "abc".



            And no, you can't call a function template template <class T> void foo(const std::basic_string<T> &s) with argument "abc". Because in order to figure out whether the parameter can be initialized by the argument, the compiler need to determine the template parameter T first. It will try to match const std::basic_string<T> & against const char [4]. And it will fail.



            The reason why it will fail is because of the template argument deduction rule. The actual rule is very complicated. But in this case, for std::basic_string<char> to be examined during the deduction, compiler will need to look for a proper "converting constructor", i.e. the constructor which can be called implicitly with argument "abc", and such lookup isn't allowed by the standard during deduction.



            Yes, it is possible to handle std::string and std::wstring in one function template:



            void foo_impl(const std::string &) {}
            void foo_impl(const std::wstring &) {}

            template <class T>
            auto foo(T &&t) {
            return foo_impl(std::forward<T>(t));
            }






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 20 at 19:33

























            answered Nov 20 at 19:05









            felix

            1,428314




            1,428314












            • std::basic_string<char> is exactly the same as std::string
              – Justin
              Nov 20 at 19:08










            • @Justin Yes, std::string is its alias. But the original question is using std::basic_string. And in this context, using std::string is inconvenient.
              – felix
              Nov 20 at 19:15


















            • std::basic_string<char> is exactly the same as std::string
              – Justin
              Nov 20 at 19:08










            • @Justin Yes, std::string is its alias. But the original question is using std::basic_string. And in this context, using std::string is inconvenient.
              – felix
              Nov 20 at 19:15
















            std::basic_string<char> is exactly the same as std::string
            – Justin
            Nov 20 at 19:08




            std::basic_string<char> is exactly the same as std::string
            – Justin
            Nov 20 at 19:08












            @Justin Yes, std::string is its alias. But the original question is using std::basic_string. And in this context, using std::string is inconvenient.
            – felix
            Nov 20 at 19:15




            @Justin Yes, std::string is its alias. But the original question is using std::basic_string. And in this context, using std::string is inconvenient.
            – felix
            Nov 20 at 19:15


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53399688%2fusing-template-to-handle-string-and-wstring%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Wiesbaden

            Marschland

            Dieringhausen