Ответ 1
Основная причина этой (стандартно-согласованной) двусмысленности, по-видимому, лежит в стоимости преобразования: разрешение перегрузки пытается минимизировать операции, выполняемые для преобразования аргумента в соответствующий параметр. Массив фактически является указателем на его первый элемент, хотя и украшен некоторой информацией типа времени компиляции. Преобразование от массива к указателю не стоит больше, чем, например, сохраняя адрес самого массива или инициализируя ссылку на него. С этой точки зрения двусмысленность кажется оправданной, хотя концептуально она неинтуитивная (и может быть подпара). Фактически, эта аргументация применяется ко всем преобразованиям Lvalue, как это предлагается в приведенной ниже цитате. Другой пример:
void g() {}
void f(void(*)()) {}
void f(void(&)()) {}
int main() {
f(g); // Ambiguous
}
Следующее обязательное стандартное. Функции, которые не являются специализациями какого-либо шаблона функции, предпочтительнее, чем те, которые являются, если обе они одинаково хорошо соответствуют (см. [Over.match.best]/(1.3), (1.6)). В нашем случае преобразование выполняется преобразованием между массивами и указателями, которое является преобразованием Lvalue с рангом Точного соответствия (согласно таблице 12 в [over.ics.user]). [Over.ics.rank]/3:
Стандартная последовательность преобразования
S1
является лучшей последовательностью преобразования, чем стандартная последовательность преобразованияS2
, если
S1
является правильной подпоследовательностьюS2
(сравнивая последовательности преобразования в канонической форме, определенные в 13.3.3.1.1, исключая любое преобразование Lvalue; последовательность преобразования идентичности рассматривается как подпоследовательность любого преобразования без идентичности последовательность) или, если не это,ранг
S1
лучше рангаS2
, илиS1
иS2
имеют одинаковый ранг и различимы по правилам в параграфе ниже, или, если не тот,[..]
Первая маркерная точка исключает наше преобразование (так как это преобразование Lvalue). Второй требует разницы в рангах, которых нет, так как оба преобразования имеют Точный рейтинг соответствия; "Правила в нижеследующем абзаце", т.е. В [over.ics.rank]/4, также не охватывают преобразования между массивами и указателями.
Так верьте или нет, ни одна из обеих последовательностей преобразования не лучше, чем другая, и, таким образом, выбирается char const*
-overload.
Возможное обходное решение: также определите вторую перегрузку как шаблон функции, затем частичное упорядочивание вставляет и выбирает первый.
template <typename T>
auto foo(T s)
-> std::enable_if_t<std::is_convertible<T, char const*>{}>
{
std::cout << "raw, size=" << std::strlen(s) << std::endl;
}
Демо.