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

时间:2015-09-11 00:10:11

标签: c++ arrays c++11

我想为这样的调用获得三个不同的函数:

foo("aa");

并且喜欢这个:

char buf[2];
foo(buf);

还有一个类似于此类调用的变体:

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

我试试这个:

//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");

但它无法编译,看起来像“a”转换为 const char *不是const char (&)[2]

但有趣的是,这样的代码编译得很好:

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

f("aaaa");

那么如何在编译时使用常量(并且在编译时知道此常量的大小)和已知大小的数组之间重载函数,而不是const?

4 个答案:

答案 0 :(得分:1)

  

那么如何在编译时使用常量(并且在编译时知道此常量的大小)和已知大小的数组之间重载函数,而不是const?

只需一次更改,您的代码就可以完美无缺,完全符合您的要求。问题是你没有正确地完成转发:

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

完美转发需要使用转发参考

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

现在,当您传递字符串文字时,arr_t将推断为const char (&)[N],并且将调用相应的foo1函数。

当然也推导出const,并且不需要foo1()的第二个参数。

  

还有一个类似于此类调用的变体:

为此添加一个与char const *匹配的重载,但会强制执行隐式转换,使其与char const (&)匹配得更差。

Alnitak

但是,如果你只是想在这三种类型之间超载,你根本不需要弄乱完美的转发位:

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

}

答案 1 :(得分:1)

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

我使用了模板专门化的另一种方法,以均衡具有const char *参数的非模板函数和具有const char (&)[S]参数的模板函数之间的推导优先级,以及与{{1 }}:

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

https://godbolt.org/z/hgs7Vh

https://rextester.com/GJZ41642

答案 2 :(得分:1)

我只是想分享我的解决方案:

f()有两种重载:一种用于char数组,另一种用于“其他所有东西”。

f2()处理“其他所有情况”。

这是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");
}

答案 3 :(得分:-1)

问题是此函数按值获取其参数:

template<typename arr_t>
void foo(arr_t arr) 

你不能按值传递数组,它们会衰减为指针,因此当你使用已知长度的数组调用它时,它会实例化foo<const char*>(const char*)foo<char*>(char*)(取决于数组的常量)并且你失去了长度。

你只想:

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