Неожиданная перегрузка вызывается при вызове функции из экземпляра шаблона

Предположим, следующий код:

#include <iostream>

template<typename... T>
void foo(const T &...);

template<unsigned N>
void foo(const char (&)[N])
{
std::cout << "char(&)[N]" << std::endl;
}

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

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

template<typename First, typename... T>
void foo(const First & first, const T &... rest)
{
std::cout << "Generic + " << sizeof...(T) << std::endl;
foo(first);
foo(rest...);
}

int main()
{
const char * c = "asdf";
char a[] = {'a', 'b', 'c', 'd'};
foo('f', c, a, 1);
foo(a);
}

Выход:

Generic + 3
Single              // fine; 'f' is `char` -> generic
Generic + 2
const char *        // fine; c is `const char *`
Generic + 1
const char *        // (!) not fine
Single
char(&)[N]          // fine; a is char[4]

Последний звонок — foo(a), где a является char[4] — называет версию, которую я ожидаю — template<unsigned N> void foo(const char (&)[N]), Но почему не создание экземпляра вариационного шаблона foo вызов foo(const char (&)[N], но звонит foo(const char *) вместо? Если не было перегрузки массива char, этого следовало ожидать — но почему это происходит здесь? не должны const First & правильно захватить тип массива?

Кроме того, что было бы самым простым способом заставить стандартную версию variadic работать должным образом с массивами, переданными ей?


Как Матье М. Заметил в комментариях, что проблема, вероятно, не в вариационных шаблонах, а в косвенность:

#include <iostream>

template <unsigned N>
void foo(const char (&)[N])
{
std::cout << "char(&)[N]" << std::endl;
}

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

template <typename T>
void goo(T const& t) {
foo(t);
}

int main()
{
char a[] = {'a', 'b', 'c', 'd'};
foo(a);
goo(a);
}
символ () [N]
const char *

Он также сказал, что это может быть ошибка компилятора — хотя код выдает точно такие же результаты как в Clang 3.2 dev, G ++ 4.6 и 4.7.

Р. Мартиньо Фернандес отметил, что меняется aнаберите в последнем фрагменте const char a[] делает код доходным const char * дважды.

10

Решение

Я думаю, что могу ответить на второй раздел: как это исправить? Я не уверен, что получил правильный ответ, потому что случай передачи строкового литерала дает, на мой взгляд, неправильный результат. Основная идея исправления состоит в том, чтобы использовать идеальную пересылку, а не надеяться, что правильный тип выводится для T const&, Зачем использовать T const& с массивом вызывает распад массива, я не совсем понял, хотя.

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

#include <iostream>
#include <utility>

void foo()
{
}

template<unsigned N>
void special(const char (&)[N])
{
std::cout << "char(&)[" << N << "]" << '\n';
}

void special(const char *)
{
std::cout << "const char *" << '\n';
}

template<typename T>
void special(const T &)
{
std::cout << "Single" << '\n';
}

template<typename First, typename... T>
void foo(First&& first, T&&... rest)
{
std::cout << "Generic + " << sizeof...(T) << '\n';
special(std::forward<First>(first));
foo(std::forward<T>(rest)...);
}

int main()
{
char const* c("foo");
char a[] = {'a', 'b', 'c', 'd'};
foo('f', "afas", a, c, 1);
foo(a);
}
5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]