Прошу прощения, если баян, но я, мягко говоря, удивлен.
Как вы считаете, имеет ли какой-нибудь смысл, например, такое объявление:
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>Прошу прощения, если баян, но я, мягко говоря, удивлен.
R>Как вы считаете, имеет ли какой-нибудь смысл, например, такое объявление: R>
R>typedef int Foo(const char*) const;
R>
R>и в какой области такое объявление может располагаться? Оказывается, имеет и может располагаться вне определения класса, не смотря на модификатор const.
Для объявления членов класса я такую фишку уже 5 лет использую, в самодельном rpc. Мне там тип члена нужен, который по другому выцарапать не удалось. Поэтому в одном макросе сначала объявляю тип члена, потом, пользуясь этим типом, саму функцию. Допускаю что кому нибудь в аналогичных целях может и вне класса пригодиться.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, rg45, Вы писали:
R>Как вы считаете, имеет ли какой-нибудь смысл, например, такое объявление: R>
R>typedef int Foo(const char*) const;
R>
R>...
Думаю, полезное применение для этой фичи найдется быстро. Например, для случаев, когда объявляется большое количество функций с одинаковой сигнатурой. Причем, прелесть в том, что один и тот же typedef одновременно используется как свободными функциями, так и функциями-членами классов, причем, разных классов! При необходимости одним махом можно изменить все объявления. Правда, определения все равно прийдется лопатить врукопашную, но тут уже копмилятор не даст ошибиться и пропустить какое-то определение.
Еще применение: это дополнительное средство повышения читабельности объявления указателей на функции члены. Например, вместо
Здравствуйте, rg45, Вы писали:
R>Прошу прощения, если баян, но я, мягко говоря, удивлен.
Интересно, почему эту фичу редко используют для безопасного объявления виртуальных функций?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, 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)
{
}
Здравствуйте, 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;
};
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, rg45, Вы писали:
R>>Прошу прощения, если баян, но я, мягко говоря, удивлен. E>Интересно, почему эту фичу редко используют для безопасного объявления виртуальных функций?
Здравствуйте, yatagarasu, Вы писали:
Y>а в чем безопасность? и опасность старого метода?
В том, что поменяешь тип функции в базе, а в каком-нибудь из наследников забудешь...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, 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 можно даже не писать в этом месте, его обычно пишут для большей наглядности
};
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, yatagarasu, Вы писали:
Y>а в чем безопасность? и опасность старого метода?
В том, что если ты ошибаешься в сигнатуре перекрытого метода, то он перестаёт быть перекрытым и становится перегруженным, скрывая оригинал.
Со всеми вытекающими.
Ну и аналогично при смене сигнатуры в базовом классе.
Здравствуйте, 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)
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 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.