简体   繁体   English

c ++重载静态const字符串与char数组

[英]c++ overload static const string vs char array

I want to get three different functions for calls like this:我想为这样的调用获得三个不同的函数:

foo("aa");

and like this one:就像这个:

char buf[2];
foo(buf);

and one more variant for calls like this one:以及类似这样的调用的另一种变体:

const char *ptr;
//set ptr
foo(ptr);//don't know buffer size at compile time

I try this:我试试这个:

//and foo1 for pointers, but how???

template<size_t N>
void foo1(char (&)[N], std::false_type)
{
  std::printf("not const\n");
}

template<size_t N>
void foo1(const char (&)[N], std::true_type)
{
   std::printf("const\n");
}

template<typename arr_t>
void foo(arr_t arr) 
{
  foo1(std::forward<arr_t>(arr), std::is_const<typename std::remove_reference<arr_t>::type>{});
}

foo("a");

But it fails to compile, looks like "a" converted to const char * not to const char (&)[2] ,但它无法编译,看起来像 "a" 转换为const char *而不是const char (&)[2]

but interesting that such code compiles fine:但有趣的是,这样的代码编译得很好:

template<size_t N>
void f(const char (&)[N])
{
}

f("aaaa");

so how can I overload function between constant at compilation time (and know size of this constant at compile time) and array with known size, but not const?那么如何在编译时的常量(并在编译时知道该常量的大小)和具有已知大小但不是常量的数组之间重载函数?

There are two issues here.这里有两个问题。 Firstly, your dispatching function is taking its argument by value.首先,您的调度函数正在按值获取其参数。 To do perfect forwarding you should take your argument by forwarding reference.要进行完美转发,您应该通过转发参考来接受您的论点。

The second issue, which surprised me, is that pointer decay takes priority over a deduced template so a function taking a pointer will be called in preference to a template taking an array.第二个问题,让我感到惊讶的是,指针衰减优先于推导模板,因此使用指针的函数将优先于使用数组的模板被调用。

You could tag dispatch using the std::is_array type trait.您可以使用std::is_array类型特征标记调度。

template<size_t N>
void foo1(char (&)[N], std::true_type)
{
  std::cout << "array\n";
}

template<size_t N>
void foo1(const char (&)[N], std::true_type)
{
   std::printf("const array\n");
}

void foo1(const char*, std::false_type)
{
   std::cout << "pointer\n";
}

template<typename T>
void foo(T&& x)
{
  foo1(std::forward<T>(x), std::is_array< typename std::remove_reference<T>::type >());
}

Live demo.现场演示。

so how can I overload function between constant at compilation time (and know size of this constant at compile time) and array with known size, but not const?那么如何在编译时的常量(并在编译时知道该常量的大小)和具有已知大小但不是常量的数组之间重载函数?

With one change, your code is perfectly fine and does exactly what you want.通过一项更改,您的代码非常好,并且完全符合您的要求。 The problem is that you're not doing perfect forwarding correctly:问题是您没有正确进行完美转发:

template<typename arr_t>
void foo(arr_t arr) 
{
  foo1(std::forward<arr_t>(arr), std::is_const<typename std::remove_reference<arr_t>::type>{});

Perfect forwarding requires the use of a forwarding reference :完美转发需要使用转发引用

template<typename arr_t>
void foo(arr_t &&arr) 
{
  foo1(std::forward<arr_t>(arr), std::is_const<typename std::remove_reference<arr_t>::type>{});

Now when you pass a string literal arr_t will deduce to const char (&)[N] , and the appropriate foo1 function will be called.现在,当您传递字符串文字时, arr_t将推断为const char (&)[N] ,并且将调用适当的foo1函数。

Of course const is also deduced and there's no need for the second parameter to foo1() .当然const也被推导出来并且不需要foo1()的第二个参数。

and one more variant for calls like this one:以及类似这样的调用的另一种变体:

For this add an overload that matches char const * , but which imposes an implicit conversion to make it a worse match than char const (&) .为此,添加一个与char const *匹配的重载,但它强加了一个隐式转换,使其成为比char const (&)更糟糕的匹配。

Live居住


However if you're just wanting to overload between these three types, you don't need to mess with the perfect forwarding bit at all:但是,如果您只是想在这三种类型之间重载,则根本不需要弄乱完美的转发位:

template<size_t N>
void foo(char (&)[N])
{
  std::printf("not const[]\n");
}

template<size_t N>
void foo(const char (&)[N])
{
   std::printf("const[]\n");
}

template<typename T>
struct dumb_ptr { T *p; dumb_ptr(T *p_) : p(p_) {} };

void foo(dumb_ptr<char const>) {
   std::printf("ptr\n");    
}

int main() {

foo("a");           // prints const []

char aa[] = "aa";
foo(aa);            // prints non const []

char const *aaa;
foo(aaa);           // prints ptr

char *aaaa;
foo(aaaa);          // prints ptr

}
  • C++11 C++11
  • msvc2015u3,gcc5.4,clang3.8.0 msvc2015u3,gcc5.4,clang3.8.0

I've used a bit another approach with template specialization to equalize deduction preorities between not template function with const char * argument and template function with const char (&)[S] argument, plus string character type tagging together with T && :我使用了另一种模板特化的方法来均衡非模板函数与const char *参数和模板函数与const char (&)[S]参数之间的推导优先级,加上字符串字符类型标记与T &&

    #include <string>
    #include <cstdio>
    #include <type_traits>

    struct tag_string{};
    struct tag_wstring{};

    template <typename t_elem, typename t_traits, typename t_alloc>
    inline constexpr const t_elem * get_c_str(const std::basic_string<t_elem, t_traits, t_alloc> & str)
    {
        return str.c_str();
    }

    template <typename CharT>
    inline constexpr CharT * get_c_str(CharT * str)
    {
        return str;
    }

    namespace detail {

        template <typename T>
        void _foo(T && s, tag_string)
        {
            // to check on convertion from string type
            static_assert(std::is_convertible<T, std::string>::value, "must be convertible to a std::string type");

            // implementation for char
            printf("%s: _foo(T &&, tag_string)\n\n", get_c_str(s));
        }

        template <typename T>
        void _foo(T && s, tag_wstring)
        {
            // to check on convertion from string type
            static_assert(std::is_convertible<T, std::wstring>::value, "must be convertible to a std::wstring type");

            // implementation for wchar_t
            printf("%ls: _foo(T &&, tag_wstring)\n\n", get_c_str(s));
        }
    }

    // for rvalues
    void foo(std::string && s)
    {
        puts("foo(std::string &&)");
        detail::_foo(s, tag_string{});
    }

    // for lvalues
    void foo(const std::string & s)
    {
        puts("foo(const std::string &)");
        detail::_foo(s, tag_string{});
    }

    // for lvalue/rvalue character arrays with compile time size
    template <size_t S>
    void foo(const char (& s)[S])
    {
        puts("foo(const char (&)[])");
        detail::_foo(s, tag_string{});
    }

    // for character pointers w/o compile time size (can be with second parameter of string runtime size)
    template <typename T>
    void foo(const T * const & s);

    // through template specialization to equalize priorities over function overloading deduction
    template<>
    void foo(const char * const & s)
    {
        puts("foo(const char * const &)");
        detail::_foo(s, tag_string{});
    }

' '

    int main()
    {
        foo("111");

        char a[] = "222";
        foo(a);

        const char a2[] = "333";
        foo(a2);

        char * b = a;
        foo(b);

        const char * b2 = "555";
        foo(b2);

        foo({'6', '6', '6', '\0'});

        foo(std::string{"777"});

        std::string s = "888";
        foo(s);
    }

Output :输出

    foo(const char (&)[])
    111: _foo(T &&, tag_string)

    foo(const char (&)[])
    222: _foo(T &&, tag_string)

    foo(const char (&)[])
    333: _foo(T &&, tag_string)

    foo(const char * const &)
    222: _foo(T &&, tag_string)

    foo(const char * const &)
    555: _foo(T &&, tag_string)

    foo(const char (&)[])
    666: _foo(T &&, tag_string)

    foo(std::string &&)
    777: _foo(T &&, tag_string)

    foo(const std::string &)
    888: _foo(T &&, tag_string)

https://godbolt.org/z/hgs7Vh https://godbolt.org/z/hgs7Vh

https://rextester.com/GJZ41642 https://rextester.com/GJZ41642

I just wanted to share my solution for this problem:我只是想分享我对这个问题的解决方案:

f() has two kinds of overloads: One for char arrays and one for "everything else". f()有两种重载:一种用于 char 数组,另一种用于“所有其他”。

f2() handles the "everything else" case. f2()处理“其他一切”的情况。

That's the most clean workaround IMHO.恕我直言,这是最干净的解决方法。

#include <cstdio>
#include <string>

template<class T>
void f2(const T& s) // Handle all kinds of string objects
{ std::printf("string object: %s\n", s.c_str()); }

void f2(const char* s) // Handle const char*
{ std::printf("const char*: %s\n", s); }

// ----------------------------------------------------------------------------

template<size_t N>
void f(const char(&s)[N]) // Handle const char array
{ std::printf("const char[%d]: %s\n", N, s); }

template<size_t N>
void f(char(&s)[N]) // Handle char array
{ std::printf("char[%d]: %s\n", N, s); }

template<class T>
inline void f(T&& s) // Handle other cases
{ f2(std::forward<T>(s)); }

int main() {
  std::string stringObj     = "some kind of string object ...";
  char charArr[]            = "char array";
  const char constCharArr[] = "const char array";
  const char* constCharPtr  = "const char pointer";

  f(stringObj);
  f(charArr);
  f(constCharArr);
  f(constCharPtr);
  //f("const char array");
}

The problem is that this function takes its argument by value:问题是这个函数按值取参数:

template<typename arr_t>
void foo(arr_t arr) 

You cannot pass arrays by value, they decay to pointers, so when you call it with an array with a known length it instantiates foo<const char*>(const char*) or foo<char*>(char*) (depending on the const-ness of the array) and you lose the length.您不能按值传递数组,它们会衰减为指针,因此当您使用已知长度的数组调用它时,它会实例化foo<const char*>(const char*)foo<char*>(char*) (取决于数组的常量性)并且您失去了长度。

You just want:你只想要:

template<size_t N>
void foo(const char (&)[N])
{
   std::printf("const array\n");
}

template<size_t N>
void foo(char (&)[N])
{
   std::printf("non-const array\n");
}

void foo(const char*)
{
   std::printf("not an array\n");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM