Разрешение перегрузки для char *, массива char и строковых литералов с использованием constexpr, SFINAE и / или type_traits - PullRequest
3 голосов
/ 06 июня 2019

Я столкнулся с интересной проблемой, которую пытался решить часами, но после долгих исследований и множества неудачных попыток я обнаружил, что задаю этот вопрос.

Я хотел бы написать 3 перегруженных функции, каждая из которых принимает один из следующих типов: const char*, const char(&)[N] и string literal (e.g. "BOO"). Я понимаю, что строковый литерал - это просто массив символов, но, пожалуйста, потерпите меня, пока я объясняю свой подход.

Две нижеприведенные функции могут различать первые два типа (const char* и const char(&)[N]) благодаря классу оболочки 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);
}

Выход:

From function that takes a CharPtrWrapper = ABC
From function that takes a "const char(&)[N]" = XYZ

Теперь, если я вызову processStr со строковым литералом (например, processStr("BOO")), будет вызвана версия, которая принимает const char(&)[N], что имеет смысл, поскольку строковый литерал является просто массивом символов.

Вот где я дохожу до сути проблемы. Я не смог написать функцию, которая могла бы различать массив символов и строковый литерал. Я подумал, что одна вещь может сработать, это написать версию, которая принимает ссылку на rvalue:

template<std::size_t N>
void processStr(const char (&&charArr)[N])
{
    std::cout << "From function that takes a \"const char(&&)[N]\" = " << charArr << '\n';
}

Но оказывается, что строковые литералы являются lvalues. Я также играл с разными версиями, которые используют std::enable_if и std::is_array, но я все еще не получаю результат, который ищу.

Итак, я думаю, что мой вопрос заключается в следующем: возможно ли провести различие между массивами символов и строковыми литералами в современном C ++?

1 Ответ

2 голосов
/ 06 июня 2019

за [expr.prim.id.unqual] :

[...] Тип выражения - это тип идентификатора. Результатом является объект, обозначенный идентификатором . Выражение lvalue, если объект является функцией, переменной или элементом данных и првалуе в противном случае; это битовое поле, если идентификатор обозначает битовое поле ([dcl.struct.bind]).

Поэтому с учетом декларации

const char arr[] = "foo";

Выражение arr является lvalue типа const char[4].


За [lex.string] / 8 :

Обычные строковые литералы и строковые литералы UTF-8 также упоминаются как узкие строковые литералы. Узкий строковый литерал имеет тип «массив n const char ”, где n - размер строки, как определено ниже, и имеет статическую продолжительность хранения.

А за [expr.prim.literal] :

A litera является основным выражением. Его тип зависит от его формы. строковый литерал является lvalue; все остальные литералы являются значениями.

Следовательно, выражение "foo" является lvalue типа const char[4].


Вывод: функция не может различить (const) массив символов и строковый литерал.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...