Не просветит ли кто-нибудь по вопросу function overloading.
Первый пример:
// test1.cppstruct S {
operator short* ();
operator char* () const;
};
void f (short*);
void f (char*);
void foo (S& s)
{
f (s);
}
g++ -c test1.cpp
test1.cpp: In function 'void foo(S&)':
test1.cpp:11: error: call of overloaded 'f(S&)' is ambiguous
test1.cpp:6: note: candidates are: void f(short int*)
test1.cpp:7: note: void f(char*)
Как я понимаю, логика следующая: S, с помощью user defined conversion, можно превратить в сhar* и в short*. Оба преобразования эквивалентны, следовательно компилятор не может выбрать лучший вариант и выдает ошибку.
Второй пример:
// test2.cppstruct B {};
struct D : private B {};
void f (B*);
void f (D*);
struct S {
operator B* () const { return 0; }
operator D* () { return 0; }
};
void foo(S & s)
{
f(s);
}
g++ -c test2.cpp
Ошибок нет. Под отладчиком проверил — выбирается f(D*). Отличие от первого примера только в том, что char* и short* можно конвертить друг в друга, а B* и D* нет. Но при чем здесь это?
Здравствуйте, acoder, Вы писали:
A>Не просветит ли кто-нибудь по вопросу function overloading.
A>Первый пример: A>Как я понимаю, логика следующая: S, с помощью user defined conversion, можно превратить в сhar* и в short*. Оба преобразования эквивалентны, следовательно компилятор не может выбрать лучший вариант и выдает ошибку.
Все верно
A>Второй пример: A>
A>// test2.cpp
A>struct B {};
A>struct D : private B {};
A>void f (B*);
A>void f (D*);
A>struct S {
A> operator B* () const { return 0; }
A> operator D* () { return 0; }
A>};
A>void foo(S & s)
A>{
A> f(s);
A>}
A>
A>
A>g++ -c test2.cpp
A>
A>Ошибок нет. Под отладчиком проверил — выбирается f(D*). Отличие от первого примера только в том, что char* и short* можно конвертить друг в друга, а B* и D* нет. Но при чем здесь это?
Нет это здесь не причем. Здесь дело в const у оператора приведения типа к B*. Если убрать этот квалификатор, будет точно такай же ambiguous call to overloaded function как и в первом примере.
Дело в том, что объект s у тебя неконстантный и предпочтение отдается неконстантным функциям.
A>Спасибо.
Здравствуйте, Greg Zubankov, Вы писали:
GZ>Дело в том, что объект s у тебя неконстантный и предпочтение отдается неконстантным функциям.
Тогда "предпочтение неконстантным функциям" должно было сработать и в первом варианте — ведь там тоже одна функция константная, а вторая — нет
GZ>Нет это здесь не причем. Здесь дело в const у оператора приведения типа к B*. Если убрать этот квалификатор, будет точно такай же ambiguous call to overloaded function как и в первом примере. GZ>Дело в том, что объект s у тебя неконстантный и предпочтение отдается неконстантным функциям.
Согласен с предыдущим оратором. Для вызова константного оператора надо применить преобразование S -> const S и в первом и во втором примере.
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Greg Zubankov, Вы писали:
GZ>>Дело в том, что объект s у тебя неконстантный и предпочтение отдается неконстантным функциям. B>Тогда "предпочтение неконстантным функциям" должно было сработать и в первом варианте — ведь там тоже одна функция константная, а вторая — нет
+1
Мот 5копеек:
константность оператора приведения играет роль ТОЛЬКО в следующем, модифицированном коде:
// test1.cppstruct S {
operator short* ();
operator char* () const;
};
void f (short*);
void f (char*);
void foo (const S& s)
{
f (s);
}
Здравствуйте, srggal, Вы писали:
S>Здравствуйте, srggal, Вы писали:
S>[]
S>Добавлю, что если Проинвертировать константность в операторах, то получаем тотже амбигоуз
Инвертировать или убрать. Я уже писал про это.
Кстати private наследование можно заменить на public. Оно ни на что не влияет.
Здравствуйте, Greg Zubankov, Вы писали:
GZ>Я уже писал про это.
Да, запамятовал.
GZ>Кстати private наследование можно заменить на public. Оно ни на что не влияет.
+1
Ок, если Вы разобрались, то ткните носом — почему такое происходит.
a> Ошибок нет. Под отладчиком проверил — выбирается f(D*). Отличие от a> первого примера только в том, что char* и short* можно конвертить друг в a> друга, а B* и D* нет. Но при чем здесь это?
Все верно. Главное отличие здесь — наследование. Дело в том, что B и D не являются эквивалентными с точки зрения перегрузки. D — better match в данном случае, т.к. наследуется от базового класса, т.е. несет более конкретную семантику чем B. Подумайте: к какому типу предпочтительнее привести объект, к базовому или конкретному?
Если бы здесь небыло наследования, то было бы тоже, что и с первым примером.
Здравствуйте, stasan, Вы писали:
S>Все верно. Главное отличие здесь — наследование. Дело в том, что B и D не являются эквивалентными с точки зрения перегрузки. D — better match в данном случае, т.к. наследуется от базового класса, т.е. несет более конкретную семантику чем B. Подумайте: к какому типу предпочтительнее привести объект, к базовому или конкретному? S>Если бы здесь небыло наследования, то было бы тоже, что и с первым примером.
Тогда почему отсутствие const у operator B* () приводит к ambiguous call?
Разбирался в стандарте. Во втором случай operator D* () является наиболее подходящей функцией из-за того что:
1) она не константная и вызывается у неконстантного объекта 13.3.3.2/3
и
2) она возвращает указатель на наиболее базовый класс 13.3.3.2/4
Именно из-за совокупности этих спецификация она и оказывается наиболее подходящей.
Здравствуйте, Greg Zubankov, Вы писали:
GZ>Разбирался в стандарте. Во втором случай operator D* () является наиболее подходящей функцией из-за того что: GZ>1) она не константная и вызывается у неконстантного объекта 13.3.3.2/3 GZ>и GZ>2) она возвращает указатель на наиболее базовый класс 13.3.3.2/4
GZ>Именно из-за совокупности этих спецификация она и оказывается наиболее подходящей.
Здравствуйте, Greg Zubankov, Вы писали:
GZ>Здравствуйте, acoder, Вы писали:
A>>Первый пример: A>>Как я понимаю, логика следующая: S, с помощью user defined conversion, можно превратить в сhar* и в short*. Оба преобразования эквивалентны, следовательно компилятор не может выбрать лучший вариант и выдает ошибку. GZ>Все верно
Это ещё почемуже?
// test1.cpp
struct S {
operator short* ();
operator char* () const;
};
void f (short*);
void f (char*);
void foo (S& s)
{
f (s);
}
g++ -c test1.cpp
test1.cpp: In function 'void foo(S&)':
test1.cpp:11: error: call of overloaded 'f(S&)' is ambiguous
test1.cpp:6: note: candidates are: void f(short int*)
test1.cpp:7: note: void f(char*)
f(char*) никакой не кондидат, т.к. ваш оператор приведения к char* работает токо с константными объектами, а неявные приведения в c++ недопустимы.
short* s = NULL;
char* p = &s; //error C2440: 'initializing' : cannot convert from 'short **__w64 ' to 'char *' (MSVC++.NET2002)
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Greg Zubankov, Вы писали:
GZ>Разбирался в стандарте. Во втором случай operator D* () является наиболее подходящей функцией из-за того что: GZ>1) она не константная и вызывается у неконстантного объекта 13.3.3.2/3 GZ>и GZ>2) она возвращает указатель на наиболее базовый класс 13.3.3.2/4
GZ>Именно из-за совокупности этих спецификация она и оказывается наиболее подходящей.
Здравствуйте, Vain, Вы писали:
A>>>Как я понимаю, логика следующая: S, с помощью user defined conversion, можно превратить в сhar* и в short*. Оба преобразования эквивалентны, следовательно компилятор не может выбрать лучший вариант и выдает ошибку. GZ>>Все верно V>Это ещё почемуже?
Тут я был не прав. short* в char* неявно не преобразовать.
V>f(char*) никакой не кондидат, т.к. ваш оператор приведения к char* работает токо с константными объектами, а неявные приведения в c++ недопустимы.
Только с константными? Т.е. test1.cpp должен компилироваться без ошибок? А если бы у S был бы только один, причем константнынй, оператор приведения типа, он никогда бы не вызвался для неконстантного объекта?
Здравствуйте, acoder, Вы писали:
A>Здравствуйте, Vain, Вы писали:
V>>f(char*) никакой не кондидат, т.к. ваш оператор приведения к char* работает токо с константными объектами, а неявные приведения в c++ недопустимы. A>Только с константными? Т.е. test1.cpp должен компилироваться без ошибок?
Я думаю да, но не исключено что вы ошиблись в другом месте, а ошибка "вылезла" здесь.
У меня были случаи когда код не компилился из-за комментария (MSVC++.NET2002) A>А если бы у S был бы только один, причем константнынй, оператор приведения типа, он никогда бы не вызвался для неконстантного объекта?
Операторы это теже самые методы, которые наследуются при наследовании (public/protected) и им свойственно, то что свойственно обычным методам, но и есть исключения. К примеру, на operator= недопустимо ставить cоnst квалификатор. Поэтому, могу сказать что над константными объектами допустимо вызывать токо методы и операторы с константными квалификаторами.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[5]: Function overloading
От:
Аноним
Дата:
20.12.05 19:04
Оценка:
Здравствуйте, Vain, Вы писали:
V>>>f(char*) никакой не кондидат, т.к. ваш оператор приведения к char* работает токо с константными объектами, а неявные приведения в c++ недопустимы. A>>Только с константными? Т.е. test1.cpp должен компилироваться без ошибок? V>Я думаю да, но не исключено что вы ошиблись в другом месте, а ошибка "вылезла" здесь. V>У меня были случаи когда код не компилился из-за комментария (MSVC++.NET2002)
test1.cpp выдает ambiguos call error и на VS.2003 и на gcc. Причины этого лучше всего описаны здесь
.
A>>А если бы у S был бы только один, причем константнынй, оператор приведения типа, он никогда бы не вызвался для неконстантного объекта? V>Операторы это теже самые методы, которые наследуются при наследовании (public/protected) и им свойственно, то что свойственно обычным методам, но и есть исключения. К примеру, на operator= недопустимо ставить cоnst квалификатор. Поэтому, могу сказать что над константными объектами допустимо вызывать токо методы и операторы с константными квалификаторами.
Тем ни менее 13 глава стандарта утверждает иное. Рассмотрим пример:
struct S
{
void f();
void f() const;
};
void foo()
{
S s1;
s1.f();
}
Компилятору надо определить, какую из двух функции вызвать для выражения s1.f(); Первая стадия — отбор кандидатов. При отборе кандидатов к функциям членам добавляется фиктивный параметр так, что функции f превращаются в:
void f(S&);
void f(S const&);
После этого (опуская некоторые детали) рассматриваются цепочки преобразований типов действительных аргументов к типам формальных аргументов. Для первой функции преобразований не требуется, т.к. s1 неконстантный объект. Для второй функции требуется преобразование S -> const S. Заметим, преобразование вполне корректное. После этого, компилятор выбирает ту функцию, для которой цепочка преобразований самая "короткая". В результате, выбирается неконстантная функция f().
Теперь уберем неконстантную функцию f() из структуры S. Очевидно будет вызываться константный метод, т.к. преобразование S -> const S корректно, а более простых цепочек нет. Что и реализовано во всех компиляторах.
Чуть не забыл, в данном случае компилятор не делает различий для операторов приведения типа и обычных методов. Все вышеприведенные рассуждения применимы и к ним.