使用 constexpr、SFINAE 和/或 type_traits 重载 char*、char 数组和字符串文字的解析 [英] Overload resolution for char*, char array, and string literals using constexpr, SFINAE and/or type_traits
问题描述
我遇到了一个有趣的挑战,我一直试图解决它几个小时,但经过大量研究和多次尝试失败后,我发现自己在问这个问题.
我想编写 3 个重载函数,每个函数采用以下类型之一:const char*
、const char(&)[N]
和 字符串文字(例如BOO")
.我知道字符串文字只是一个字符数组,但在我解释我的方法时请耐心等待.
由于包装类,下面的两个函数能够区分前两种类型(const char*
和 const char(&)[N]
)CharPtrWrapper
:
#include 类 CharPtrWrapper{上市:CharPtrWrapper(const char* charPtr): m_charPtr(charPtr){}const char * m_charPtr;};void processStr(CharPtrWrapper charPtrWrapper){std::cout <<"来自采用 CharPtrWrapper = " << 的函数charPtrWrapper.m_charPtr <<'\n';}模板void processStr(const char (&charArr)[N]){std::cout <<"来自采用 \"const char(&)[N]\" = " << 的函数charArr <<'\n';}int main(){const char* charPtr = "ABC";processStr(charPtr);const char charArr[] = {'X', 'Y', 'Z', '\0'};processStr(charArr);}
输出:
From 接受 CharPtrWrapper = ABC 的函数来自采用const char(&)[N]" = XYZ 的函数
现在,如果我使用字符串文字(例如 processStr("BOO")
)调用 processStr
,则该版本采用 const char(&)[N]
被调用,这是有道理的,因为字符串文字只是一个字符数组.
这里是我解决问题的关键所在.我一直无法编写一个能够区分字符数组和字符串文字的函数.我认为可能可行的一件事是编写一个接受右值引用的版本:
templatevoid processStr(const char (&&charArr)[N]){std::cout <<"来自采用 \"const char(&&)[N]\" = " << 的函数charArr <<'\n';}
但事实证明,字符串文字是左值.我也玩过使用 std::enable_if
和 std::is_array
的不同版本,但我仍然没有得到我想要的结果.>
所以我想我的问题如下:是否有可能在现代 C++ 中区分字符数组和字符串文字?
[...] 表达式的类型是标识符的类型.这结果是由标识符表示的实体.表达式是一个lvalue 如果实体是函数、变量或数据成员,并且否则右值;如果标识符指定一个位域位域([dcl.struct.bind]).
因此,给出一个声明
const char arr[] = "foo";
表达式 arr
是 const char[4]
类型的左值.
根据 [lex.string]/8:
<块引用>普通字符串文字和UTF-8字符串文字也被引用作为窄字符串文字.窄字符串文字的类型为数组n const char
",其中 n 是定义的字符串大小下面,并具有静态存储期限.
A litera 是主要表达方式.它的类型取决于它的形式.一种字符串文字是左值;所有其他文字都是纯右值.
因此,表达式 "foo"
是 const char[4]
类型的左值.
结论:函数无法区分(const)字符数组和字符串文字.
I have run into an interesting challenge that I have been trying to solve for hours, but after much research and many failed attempts, I find myself asking this question.
I would like to write 3 overloaded functions that each take one of the following types: const char*
, const char(&)[N]
and string literal (e.g. "BOO")
. I understand that a string literal is simply a char array, but please bear with me while I explain my approach.
The two functions below are able to differentiate between the first two types (const char*
and const char(&)[N]
) thanks to the wrapper class CharPtrWrapper
:
#include <iostream>
class CharPtrWrapper
{
public:
CharPtrWrapper(const char* charPtr)
: m_charPtr(charPtr)
{
}
const char * m_charPtr;
};
void processStr(CharPtrWrapper charPtrWrapper)
{
std::cout << "From function that takes a CharPtrWrapper = " << charPtrWrapper.m_charPtr << '\n';
}
template<std::size_t N>
void processStr(const char (&charArr)[N])
{
std::cout << "From function that takes a \"const char(&)[N]\" = " << charArr << '\n';
}
int main()
{
const char* charPtr = "ABC";
processStr(charPtr);
const char charArr[] = {'X', 'Y', 'Z', '\0'};
processStr(charArr);
}
Output:
From function that takes a CharPtrWrapper = ABC
From function that takes a "const char(&)[N]" = XYZ
Now, if I call processStr
with a string literal (e.g. processStr("BOO")
), the version that takes a const char(&)[N]
gets called, which makes sense, since a string literal is simply a char array.
Here is where I reach the crux of the problem. I have not been able to write a function that is able to differentiate between a char array and a string literal. One thing I thought might work was to write a version that takes an rvalue reference:
template<std::size_t N>
void processStr(const char (&&charArr)[N])
{
std::cout << "From function that takes a \"const char(&&)[N]\" = " << charArr << '\n';
}
But it turns out that string literals are lvalues. I have also played with different versions that use std::enable_if
and std::is_array
, but I still don't get the result I'm looking for.
So I guess my question is the following: is it possible to differentiate between char arrays and string literals in modern C++?
[...] The type of the expression is the type of the identifier. The result is the entity denoted by the identifier. The expression is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise; it is a bit-field if the identifier designates a bit-field ([dcl.struct.bind]).
Therefore, given a declaration
const char arr[] = "foo";
The expression arr
is an lvalue of type const char[4]
.
Per [lex.string]/8:
Ordinary string literals and UTF-8 string literals are also referred to as narrow string literals. A narrow string literal has type "array of n
const char
", where n is the size of the string as defined below, and has static storage duration.
And per [expr.prim.literal]:
A litera is a primary expression. Its type depends on its form. A string literal is an lvalue; all other literals are prvalues.
Therefore, the expression "foo"
is an lvalue of type const char[4]
.
Conclusion: a function is unable to differentiate between a (const) char array and a string literal.
这篇关于使用 constexpr、SFINAE 和/或 type_traits 重载 char*、char 数组和字符串文字的解析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!