Прошу прощения, если баян, но я, мягко говоря, удивлен.
Как вы считаете, имеет ли какой-нибудь смысл, например, такое объявление:
typedef int Foo(const char*) const;
и в какой области такое объявление может располагаться? Оказывается, имеет и может располагаться вне определения класса, не смотря на модификатор const.
А вот вполне себе well-formed программка, можете откомпилировать и запустить. Обратите внимание на объявления и определения свободных функций и функций-членов:
А вот пункт стандарта, который все это благословляет: 9.3/9. Для тех, у кого нет под рукой стандарта цитирую:
[Note: a member function can be declared (but not defined) using a typedef for a function type. The resulting member function has exactly the same type as it would have if the function declarator were provided explicitly, see 8.3.5. For example,
Здравствуйте, rg45, Вы писали:
R>Прошу прощения, если баян, но я, мягко говоря, удивлен.
Интересно, почему эту фичу редко используют для безопасного объявления виртуальных функций?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Здравствуйте, rg45, Вы писали:
R>>Тебя удивляет, что компилятор не возражает против определения чисто виртуальной функции?
PD>Возражает
1>>e:\45555252\1.cpp(11) : error C2259: 'A' : cannot instantiate abstract class 1>> due to following members: 1>> 'void A::f(void)' : is abstract 1>> e:\45555252\1.cpp(2) : see declaration of 'A::f'
>>Ты еще больше удивишься когда узнаешь, что это полностью соответствет стандарту, см. 10.4/2.
PD>Значит, VC 2008 не соответствует стандарту
>>... можно определить чисто виртуальную функцию. Только, на всякий случай, определение чисто виртуальной функции болжно быть выполнено за пределами определения класса согласно все тому же пункту стандарта.
PD>Как видишь, в VC2008 нельзя.
Ну у нас получается разговор слепого с глухим, честное слово. Обрати внимание на выделенные фразы. Я говорю: "можно определять чисто виртуальные функции", а ты мне возражаешь: "нет, объекты абстрактных классов создавать нельзя" . На самом деле оба эти утверждения верны и друг другу не противоречат. Объекты абстрактных классов действительно нельзя создавать, но при этом можно давать определения чисто виртуальных функций классов этих объектов.
А все недоразумение началось из-за неполноты приведенного тобой примера: здесь
. По этому фрагменту и комментариям в нем можно сделать только один вывод, что ты не знаешь о том, что у чисто виртуальных функций могут быть определения. Но, как я теперь уже понимаю благодаря постам других участников, ты хотел именно сказать о глюках майкрософтовских компиляторов, которые пропускают создание объектов абстрактных классов. Это действительно ценные сведения, спасибо.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Привет всем!
R>Прошу прощения, если баян, но я, мягко говоря, удивлен.
R>Как вы считаете, имеет ли какой-нибудь смысл, например, такое объявление: R>
R>typedef int Foo(const char*) const;
R>
R>и в какой области такое объявление может располагаться? Оказывается, имеет и может располагаться вне определения класса, не смотря на модификатор const.
gcc 3.4.6 не поддерживает консты
e.cpp:189: error: invalid type qualifier for non-member function type
e.cpp:189: error: `const' and `volatile' function specifiers on `Foo' invalid in type declaration
Как обычно, макросы нас спасут (и заодно будут работать и для определений, не только для объявлений) :
#define DECLARE(sig, name) sig(,name)
#define DEFINE(sig, class, name) sig(class::,name)
// signatures#define SIG1(class, name) void class name(int i, double d)
#define SIG2(class, name) void class name(double d, int i) const// better name for SIG1#define DECLARE_CALLBACK(name) DECLARE(SIG1, name)
#define DEFINE_CALLBACK(class, name) DEFINE(SIG1, class, name)
struct A
{
DECLARE(SIG1, f1);
DECLARE(SIG2, g);
DECLARE_CALLBACK(f2);
};
DEFINE(SIG1, A, f1)
{
}
DEFINE(SIG2, A, g)
{
}
DEFINE_CALLBACK(A, f2)
{
}
брюки превращаются вот в такой код:
struct A
{
void f1(int i, double d);
void g(double d, int i) const;
void f2(int i, double d);
};
void A:: f1(int i, double d)
{
}
void A:: g(double d, int i) const
{
}
void A:: f2(int i, double d)
{
}
Здравствуйте, Alexey F, Вы писали:
AF>Здравствуйте, rg45, Вы писали:
AF>...И, конечно же , такое: AF>
AF>// Называется "попробуй не специально сделать различные параметры по-умолчанию у предка
AF>// и наследника в виртуальных функциях при соблюдении такого синтаксиса":
AF>typedef void SomeMethod ( int = 42 ) const;
AF>struct A {
AF> virtual SomeMethod m = 0;
AF>};
AF>struct B : A {
AF> virtual SomeMethod m;
AF>};
AF>void B::m ( int ) const {
AF>}
AF>
AF>ИМХО, такие typedef-ы (я не про параметры по-умолчанию, а вообще) — очень крутая фича
Параметры по умолчанию (как и exception specification) не могут быть использованы в таком контексте (8.3.6/3, 15.4/1).
Здравствуйте, yatagarasu, Вы писали:
Y>Здравствуйте, Erop, Вы писали:
E>>Здравствуйте, rg45, Вы писали:
R>>>Прошу прощения, если баян, но я, мягко говоря, удивлен. E>>Интересно, почему эту фичу редко используют для безопасного объявления виртуальных функций?
Y>а в чем безопасность? и опасность старого метода?
В том, что легко допустить незначительную ошибку в сигнатуре и вместо переопределения виртуальной функции получить сокрытие, например:
class A
{
public:
virtual int foo(int) const;
};
class B : public A
{
public:
virtual int foo(int); //Забыли модификатор const и получили новую функцию вместо перекрытия существующей
};
И хорошо, если компилятор выдаст предупреждение.
А такой вариант дает больше уверенности:
typedef int Foo(int) const;
class A
{
public:
virtual Foo foo;
};
class B : public A
{
public:
virtual Foo foo; //virtual можно даже не писать в этом месте, его обычно пишут для большей наглядности
};
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, Erop, Вы писали:
E>>Здравствуйте, rg45, Вы писали:
R>>>Прошу прощения, если баян, но я, мягко говоря, удивлен. E>>Интересно, почему эту фичу редко используют для безопасного объявления виртуальных функций?
R>Я вообще никогда раньше не встречал таких объявлений функций-членов, ни виртуальных ни невиртуальных. А выглядит, конечно, непривычно: R>
R>struct A
R>{
R> virtual Foo foo = 0;
R>};
R>
Гм...
Берем твой пример и делаем так
struct A
{
virtual Foo h = 0;
Foo f1;
Foo f2;
1>4777777.obj : error LNK2001: unresolved external symbol "public: virtual class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall A::h(char const *)" (?h@A@@UAE?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@PBD@Z)
R>Как вы считаете, имеет ли какой-нибудь смысл, например, такое объявление: R>
R>typedef int Foo(const char*) const;
R>
R>и в какой области такое объявление может располагаться? Оказывается, имеет и может располагаться вне определения класса, не смотря на модификатор const.
Кстати, в Active Issue 547 предлагается ослабить ограничение на допустимые места, в которых можно использовать квалификатор const и разрешить такое:
template<class T> struct X;
// partial specialization for a const member function:template<class R> struct X< R() const >
{};
R>А вот пункт стандарта, который все это благословляет: 9.3/9.
Не совсем: 8.3.5/4 разрешает использование cv-qualifier в typedef declaration для top-level function type, а 8.3.5/7 разрешает использовать объявленные таким образом имена при объявлении функций-членов. С volatile такой фокус также проходит.
Здравствуйте, rg45, Вы писали:
R>Прошу прощения, если баян, но я, мягко говоря, удивлен.
R>Как вы считаете, имеет ли какой-нибудь смысл, например, такое объявление: R>
R>typedef int Foo(const char*) const;
R>
R>и в какой области такое объявление может располагаться? Оказывается, имеет и может располагаться вне определения класса, не смотря на модификатор const.
Для объявления членов класса я такую фишку уже 5 лет использую, в самодельном rpc. Мне там тип члена нужен, который по другому выцарапать не удалось. Поэтому в одном макросе сначала объявляю тип члена, потом, пользуясь этим типом, саму функцию. Допускаю что кому нибудь в аналогичных целях может и вне класса пригодиться.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, rg45, Вы писали:
R>Прошу прощения, если баян, но я, мягко говоря, удивлен. R>...
Еще маленький нюанс. Также, как для объявленных "традиционным" образом перегруженных функций-членов, при таком способе объявления также допустимо повторное использование идентификаторов:
typedef void X();
typedef void Y() const;
typedef void Z(int);
struct A
{
X foo;
Y foo;
Z foo;
};
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, rg45, Вы писали:
R>>Прошу прощения, если баян, но я, мягко говоря, удивлен. E>Интересно, почему эту фичу редко используют для безопасного объявления виртуальных функций?
Я вообще никогда раньше не встречал таких объявлений функций-членов, ни виртуальных ни невиртуальных. А выглядит, конечно, непривычно:
struct A
{
virtual Foo foo = 0;
};
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, yatagarasu, Вы писали:
Y>а в чем безопасность? и опасность старого метода?
В том, что если ты ошибаешься в сигнатуре перекрытого метода, то он перестаёт быть перекрытым и становится перегруженным, скрывая оригинал.
Со всеми вытекающими.
Ну и аналогично при смене сигнатуры в базовом классе.
Здравствуйте, rg45, Вы писали:
R>Тебя удивляет, что компилятор не возражает против определения чисто виртуальной функции?
Возражает
class A{
virtual void f() = 0;
};
void A::f()
{
}
int main()
{
A a;
return 0;
}
1>e:\45555252\1.cpp(11) : error C2259: 'A' : cannot instantiate abstract class 1> due to following members: 1> 'void A::f(void)' : is abstract 1> e:\45555252\1.cpp(2) : see declaration of 'A::f'
>Ты еще больше удивишься когда узнаешь, что это полностью соответствет стандарту, см. 10.4/2.
.А еще можешь переделать свой пример для "традиционнго" объявления, ты убедишься, что в этом случае можно определить чисто виртуальную функцию. Только, на всякий случай, определение чисто виртуальной функции болжно быть выполнено за пределами определения класса согласно все тому же пункту стандарта.
Здравствуйте, rg45, Вы писали:
R>Как вы считаете, имеет ли какой-нибудь смысл, например, такое объявление: R>
R>typedef int Foo(const char*) const;
R>
R>...
Думаю, полезное применение для этой фичи найдется быстро. Например, для случаев, когда объявляется большое количество функций с одинаковой сигнатурой. Причем, прелесть в том, что один и тот же typedef одновременно используется как свободными функциями, так и функциями-членами классов, причем, разных классов! При необходимости одним махом можно изменить все объявления. Правда, определения все равно прийдется лопатить врукопашную, но тут уже копмилятор не даст ошибиться и пропустить какое-то определение.
Еще применение: это дополнительное средство повышения читабельности объявления указателей на функции члены. Например, вместо
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, rg45, Вы писали:
R>>Прошу прощения, если баян, но я, мягко говоря, удивлен. E>Интересно, почему эту фичу редко используют для безопасного объявления виртуальных функций?
Здравствуйте, yatagarasu, Вы писали:
Y>а в чем безопасность? и опасность старого метода?
В том, что поменяешь тип функции в базе, а в каком-нибудь из наследников забудешь...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
PD>========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
PD>Это как все понимать ?
Тебя удивляет, что компилятор не возражает против определения чисто виртуальной функции? Ты еще больше удивишься когда узнаешь, что это полностью соответствет стандарту, см. 10.4/2. Этот вопрос не раз обсуждался на форума и в Q&A есть ссылка: А чисто виртуальные деструкторы — бывают?
.А еще можешь переделать свой пример для "традиционнго" объявления, ты убедишься, что в этом случае можно определить чисто виртуальную функцию. Только, на всякий случай, определение чисто виртуальной функции болжно быть выполнено за пределами определения класса согласно все тому же пункту стандарта.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Alexander G, Вы писали:
AG>Здравствуйте, Pavel Dvorkin, Вы писали:
PD>>Это как все понимать ?
AG>Нужно полностью код приводить AG>тут баг не то что бывают чисто виртуальные функции, а то, что с ними можно создать экземпляр класса.
AG>pure specifier, видимо, почти игнорируется.
AG>
AG>typedef void G (int);
AG>struct X
AG>{
AG> G f1 = 0;
AG>};
AG>int main()
AG>{
AG> X y;
AG>}
AG>
AG>и даже
AG>
AG>typedef void G (int);
AG>union X
AG>{
AG> G f1 = 666;
AG>};
AG>int main()
AG>{
AG> X y;
AG>}
AG>
В каком компиляторе? У меня в mingw 4.4 все прекрасно. Выдает ошибку как и положено.
Здравствуйте, Alexander G, Вы писали:
AG>Здравствуйте, wander, Вы писали:
W>>В каком компиляторе? У меня в mingw 4.4 все прекрасно. Выдает ошибку как и положено.
AG>default compiler
AG>конкретно, в моём случае MSVC8 (VC 2005).
Подозреваю, что эта проблема наблюдается только в MSVC.
Здравствуйте, Alexander G, Вы писали:
W>>В каком компиляторе? У меня в mingw 4.4 все прекрасно. Выдает ошибку как и положено.
AG>default compiler
AG>конкретно, в моём случае MSVC8 (VC 2005).
7.1 кажись ругался.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
// Называется "попробуй не специально сделать различные параметры по-умолчанию у предка
// и наследника в виртуальных функциях при соблюдении такого синтаксиса":typedef void SomeMethod ( int = 42 ) const;
struct A {
virtual SomeMethod m = 0;
};
struct B : A {
virtual SomeMethod m;
};
void B::m ( int ) const {
}
ИМХО, такие typedef-ы (я не про параметры по-умолчанию, а вообще) — очень крутая фича
. По этому фрагменту и комментариям в нем можно сделать только один вывод, что ты не знаешь о том, что у чисто виртуальных функций могут быть определения.
Определения быть могут, и это я знаю, но от того, что у чисто виртуальной функции появится определение, класс, содержащий такую функцию, не перестанет быть абстрактным , и инстансы его создавать нельзя. VC 2008 вполне следует этому правилу для явно описанных чисто виртуальных функций и не следует для описанных через этот самый typedef.
>Но, как я теперь уже понимаю благодаря постам других участников, ты хотел именно сказать о глюках майкрософтовских компиляторов, которые пропускают создание объектов абстрактных классов.
Дело не в том, что он пропускает. Он не считает этот класс абстрактным!!!
class A {
virtual void f() = 0;
};
void A::f()
{
}
int _tmain(int argc, _TCHAR* argv[])
{
A a;
return 0;
}
Описание чисто вирт. функции есть, но тем не менее класс абстрактный
1>e:\4111111\4111111.cpp(16) : error C2259: 'A' : cannot instantiate abstract class 1> due to following members: 1> 'void A::f(void)' : is abstract 1> e:\4111111\4111111.cpp(7) : see declaration of 'A::f'
А теперь
typedef void F(void);
class A {
virtual F f = 0;
};
void A::f()
{
}
int _tmain(int argc, _TCHAR* argv[])
{
A a;
return 0;
}
и программа замечательно начинает выполняться и экземпляр A a нормально создается!
Здравствуйте, Pavel Dvorkin, Вы писали:
>>Но, как я теперь уже понимаю благодаря постам других участников, ты хотел именно сказать о глюках майкрософтовских компиляторов, которые пропускают создание объектов абстрактных классов.
PD>Дело не в том, что он пропускает. Он не считает этот класс абстрактным!!!
Не пойму я что-то. Каковы отличительные признаки "допускает создание объектов абстрактного класса" от "не считает класс абстрактным"?
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Не пойму я что-то. Каковы отличительные признаки "допускает создание объектов абстрактного класса" от "не считает класс абстрактным"?
А что же тут непонятного ? Если компилятор считает, что класс абстрактный, но при этом допускает создание экземпляров — это одно, это явная ошибка компилятора. Если же компилятор не считает класс абстрактным, тогда создавать экземпляры он имеет полное право, а вопрос же — верно ли он так считает ?