Передать ссылку на массив в С++
Может ли кто-нибудь помочь мне понять следующий код
#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 *
? Как я могу заставить его вызвать эталонную версию?
Ответы
Ответ 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 ошибается во второй раз.