Подтвердить что ты не робот

Передать ссылку на массив в С++

Может ли кто-нибудь помочь мне понять следующий код

#include <iostream>

void foo(const char * c)
{
   std::cout << "const char *" << std::endl;
}

template <size_t N>
void foo(const char (&t) [N])
{
   std::cout << "array ref" << std::endl;
   std::cout << sizeof(t) << std::endl;
}

int main()
{
    const char t[34] = {'1'};
    foo(t);

    char d[34] = {'1'};
    foo(d);
}

Выходной сигнал

const char *
array ref
34

Почему первый foo вызывает версию const char *? Как я могу заставить его вызвать эталонную версию?

4b9b3361

Ответ 1

Преобразование const char[N] в const char* считается "точным соответствием" (чтобы упростить литералы), а между двумя точными совпадениями имеет преимущество не-шаблонная функция.

Вы можете использовать enable_if и is_array, чтобы заставить его делать то, что вы хотите.


Непристойным способом заставить его может быть:

#include <iostream>

template <typename T>
void foo(const T* c)
{
   std::cout << "const T*" << std::endl;
}

template <typename T, size_t N>
void foo(const T (&t) [N])
{
   std::cout << "array ref" << std::endl;
}

int main()
{
    const char t[34] = {'1'};
    foo(t);

    char d[34] = {'1'};
    foo(d);
}

/*
array ref
array ref
*/

Я понимаю, что OP имел char не какой-то общий T, но тем не менее это демонстрирует, что проблема заключается в одной перегрузке, являющейся шаблоном, а не другом.

Ответ 2

Посмотрите на этот модифицированный пример без шаблона.

void foo(const char * c)
{
    std::cout << "const char *" << std::endl;
}

void foo(const char (&t) [34])
{
    std::cout << "const char (&) [34]" << std::endl;
}

int main()
{
    const char t[34] = {'1'};
    foo(t);
}

Мой компилятор говорит, что вызов перегруженного foo неоднозначен. Это связано с тем, что преобразования из массива в указатель считаются "точной" последовательностью преобразования и не лучше, чем нулевая последовательность преобразования для разрешения перегрузки (стандартный раздел 13.3.3.1.1.)

В исходном коде параметр шаблона N может быть вычислен как 34, но тогда как без шаблона foo(const char*), так и foo<34>(const char (&)[34]) рассматриваются в разрешении перегрузки. Поскольку ни один из них не лучше других правил преобразования, функция без шаблона выполняет функцию шаблона.

Фиксирование вещей кажется сложным. Кажется, что шаблон is_array из заголовка <type_traits> (из С++ 0x, если возможно, или Boost if not) может помочь.

Ответ 3

Это выглядит по-разному для разных компиляторов.

Mircosoft и Borland используют версию const char *, в то время как GNU дает описание, которое вы описали.

Вот фрагмент из стандарта С++:

14.8.2.1 Вывод аргументов шаблона из вызова функции [Temp.deduct.call]

Вычисление аргумента шаблона выполняется сравнение каждого шаблона функции тип параметра (назовите его P) с помощью тип соответствующего аргумента вызов (вызовите его A), как описано ниже.

Если P не является ссылочным типом:

- Если A является типом массива, тип указателя, создаваемый от массива к указателю стандартное преобразование (4.2) используется в место вывода типа А; в противном случае,

- Если A - тип функции, тип указателя, созданный стандарт функции-указатель преобразование (4.3) используется вместо A для вывода типа; в противном случае,

- Если A - это класс с квалификацией cv, верхние уровни cv-квалификаторы типа A игнорируются для вывода типа.

Если P - это класс с квалификацией cv, верхний уровня cv-квалификаторы типа P игнорируется для вывода типа. Если P является ссылочный тип, тип, упомянутый по P используется для вывода типа

Компилятор построит список A следующим образом:

Argument:        t                 d
A:          char const[34]      char[34]

И список параметров P:

Parameter:       c                 t
P:            char const*       char const& t[N]

По умолчанию компилятор должен выбрать параметры без ссылки. По какой-то причине GNU ошибается во второй раз.