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.