我想为这样的调用获得三个不同的函数:
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?
答案 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 (&)
匹配得更差。
但是,如果你只是想在这三种类型之间超载,你根本不需要弄乱完美的转发位:
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)
我使用了模板专门化的另一种方法,以均衡具有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);
}
答案 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");
}