使用 constexpr、SFINAE 和/或 type_traits 重载 char*、char 数组和字符串文字的解析 [英] Overload resolution for char*, char array, and string literals using constexpr, SFINAE and/or type_traits

查看:74
本文介绍了使用 constexpr、SFINAE 和/或 type_traits 重载 char*、char 数组和字符串文字的解析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个有趣的挑战,我一直试图解决它几个小时,但经过大量研究和多次尝试失败后,我发现自己在问这个问题.

我想编写 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_ifstd::is_array 的不同版本,但我仍然没有得到我想要的结果.

所以我想我的问题如下:是否有可能在现代 C++ 中区分字符数组和字符串文字?

解决方案

Per [expr.prim.id.unqual]:

<块引用>

[...] 表达式的类型是标识符的类型.这结果是由标识符表示的实体.表达式是一个lvalue 如果实体是函数、变量或数据成员,并且否则右值;如果标识符指定一个位域位域([dcl.struct.bind]).

因此,给出一个声明

const char arr[] = "foo";

表达式 arrconst char[4] 类型的左值.

<小时>

根据 [lex.string]/8:

<块引用>

普通字符串文字和UTF-8字符串文字也被引用作为窄字符串文字.窄字符串文字的类型为数组n const char",其中 n 是定义的字符串大小下面,并具有静态存储期限.

根据 [expr.prim.literal]:

<块引用>

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++?

解决方案

Per [expr.prim.id.unqual]:

[...] 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆