Имеется следующий код:
void f(char(&)[8]);
void f(char*);
int main()
{
char b[8];
f(b);
}
Visual ругается на него (аналогично ведет себя и g++):
test.cpp(6) : error C2668: 'f' : ambiguous call to overloaded function
test.cpp(2): could be 'void f(char *)'
test.cpp(1): or 'void f(char (&)[8])'
while trying to match the argument list '(char [8])'
Кто-нибудь может объяснить почему? Ведь первый вариант функции
f не требует никакой конверсии аргументов, а второй вариант требует преобразования
array-to-pointer (4.2 из стандарта).
Здравствуйте, tcoder, Вы писали:
T>Имеется следующий код:
T>T>void f(char(&)[8]);
T>void f(char*);
T>int main()
T>{
T> char b[8];
T> f(b);
T>}
T>
T>Visual ругается на него (аналогично ведет себя и g++):
T>T>test.cpp(6) : error C2668: 'f' : ambiguous call to overloaded function
T> test.cpp(2): could be 'void f(char *)'
T> test.cpp(1): or 'void f(char (&)[8])'
T> while trying to match the argument list '(char [8])'
T>
T>Кто-нибудь может объяснить почему? Ведь первый вариант функции f не требует никакой конверсии аргументов, а второй вариант требует преобразования array-to-pointer (4.2 из стандарта).
Возможно преобразование char[]->char* не считается за преобразование вовсе, а является не влияющим на последовательность выбора функций приведением, т.е.
struct MYCLASS {
MYCLASS(void*) {}
};
void f(MYCLASS);
int main(int argc,char* argv[]) {
f((int*)0);
return 0;
}
Здесь 2 преобразования: int*->void* и void*->MYCLASS(void*)
вот char[]->char* здесь аналогично int*->void*
И если я уберу это приведение во второй функции:
struct MYCLASS {
MYCLASS(void*) {}
};
struct MYCLASS2 {
MYCLASS2(int*) {}
};
void f(MYCLASS);
void f(MYCLASS2);
int main(int argc,char* argv[]) {
f((int*)0);
return 0;
}
то будет похожая ситуация
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, tcoder, Вы писали:
T>Кто-нибудь может объяснить почему? Ведь первый вариант функции f не требует никакой конверсии аргументов, а второй вариант требует преобразования array-to-pointer (4.2 из стандарта).
У принципе так можно
void f(char *&x){};
void f(char (&x)[8]){}
А вообщето ссылка значением не является ее нельзя скопировать только создать ИМХО здесь создание ссылки в стеке, впрочем, это теология
Здравствуйте, tcoder, Вы писали:
T>Имеется следующий код:
T>T>void f(char(&)[8]);
T>void f(char*);
T>int main()
T>{
T> char b[8];
T> f(b);
T>}
T>
T>Visual ругается на него (аналогично ведет себя и g++):
T>T>test.cpp(6) : error C2668: 'f' : ambiguous call to overloaded function
T> test.cpp(2): could be 'void f(char *)'
T> test.cpp(1): or 'void f(char (&)[8])'
T> while trying to match the argument list '(char [8])'
T>
T>Кто-нибудь может объяснить почему? Ведь первый вариант функции f не требует никакой конверсии аргументов, а второй вариант требует преобразования array-to-pointer (4.2 из стандарта).
Ранжирование преобразований производится согласно их рангу, приведенному в 13.3.3.1.1/3 Table 9. Согласно этой таблице, преобразования lvalue-to-rvalue, array-to-pointer, function-to-pointer и qualification имеют тот же ранг (exact match), что и полное отсутствие какого-либо преобразования вообще. Поэтому с точки зрения overload resolution два варианта в примере выше равноценны.
Здравствуйте, Андрей Тарасевич, Вы писали:
T>>Кто-нибудь может объяснить почему? Ведь первый вариант функции f не требует никакой конверсии аргументов, а второй вариант требует преобразования array-to-pointer (4.2 из стандарта).
АТ>Ранжирование преобразований производится согласно их рангу, приведенному в 13.3.3.1.1/3 Table 9. Согласно этой таблице, преобразования lvalue-to-rvalue, array-to-pointer, function-to-pointer и qualification имеют тот же ранг (exact match), что и полное отсутствие какого-либо преобразования вообще. Поэтому с точки зрения overload resolution два варианта в примере выше равноценны.
если бы это было так, то f(int*) и f(const int*) для вызова f(&some_int_variable) тоже давали бы неоднозначность, потому что в одном случае преобразование отсутствует, а в другом — Qualification.
На самом деле ранг вступает в игру только после того, как было выяснено, что одна последовательность преобразований не является точной подпоследовательностью другой, при этом специально сделана оговорка, что Identity является подпоследовательностью любой другой последовательности. У нас же для первой функции Identity, а для второй — Lvalue Transformation, т.е. первая является точной подпоследовательностью второй. Но, к сожалению, там есть еще одна оговорка (выделена), которая портит все дело:
— Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence
S2 if
— S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form
defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is
considered to be a subsequence of any non-identity conversion sequence) or, if not that,
— the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable
by the rules in the paragraph below, or, if not that,
... (другие пункты, которые к нам не относятся)
таким образом Lvalue Transformation выбрасывается и получаются две одинаковых последовательности, после чего мы видим, что первый пункт к нам не применим, и переходим к пункту 2, в котором идет сравнение по рангу — ранг одинаковый, идем на следующие пункты (различие только в квалификации и случай, когда обе цепочки дают ссылки) оба пункта не по нашу душу, стало быть, преобразования неразличимы.
А вот если бы там было не Lvalue Transformation, а Qualification, которую для сравнения по первому пункту выбрасывать нельзя, то мы бы остановились еще на первом пункте и разрешили бы перегрузку в пользу Identity.
Короче говоря, если цепочки оказались разных рангов, то этого достаточно для принятия решения в пользу одной из них, а если в одном ранге, то возможны варианты, потому что остальные пункты могут сработать (тогда перегрузка разрешается) или не сработать (тогда получаем неоднозначность).