В C ++ отсутствует эквивалент ключевого слова PHPself
, которое оценивает тип включающего класса.
Достаточно легко подделать это для каждого класса:
struct Foo
{
typedef Foo self;
};
но мне пришлось писать Foo
снова. Возможно, однажды я сделаю это неправильно и вызову тихую ошибку.
Могу ли я использовать комбинацию из decltype
и друзей, чтобы эта работа работала "автономно"? Я уже пробовал следующее, но this
в этом месте недопустимо:
struct Foo
{
typedef decltype(*this) self;
};
// main.cpp:3:22: error: invalid use of 'this' at top level
// typedef decltype(*this) self;
(Я не буду беспокоиться об эквиваленте static
, который делает то же самое, но с поздним связыванием.)
this_t
вероятно, будет больше соответствовать обычному именованию C ++.auto()
и~auto()
для ctors / dtors. По меньшей мере интересно. Возможноtypedef auto self;
, если использовать для этой цели, но мне это кажется немного схематичным.decltype(class)
, возможно, сdecltype(struct)
эквивалентом. Это намного яснее, чем простоauto
в конкретном контексте, и я не вижу никаких проблем с тем, чтобы это соответствовало языку, основанному наdecltype(auto)
.void _check() { static_assert(std::is_same<self&, decltype(*this)>::value, "Correct your self type"); }
не работает с шаблонами классов, хотя ...Ответы:
Вот как это можно сделать, не повторяя тип Foo:
Если вы хотите наследовать,
Foo
вы должны использовать макросWITH_SELF_DERIVED
следующим образом:Вы даже можете выполнять множественное наследование с любым количеством базовых классов (благодаря вариативным шаблонам и вариативным макросам):
Я проверил, что это работает на gcc 4.8 и clang 3.4.
источник
auto
иdecltype
или в этом случаеself
.template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};
было бы проще и позволило бы более точно контролировать наследование - есть ли причины против?Возможный обходной путь (поскольку вам все равно нужно написать тип один раз):
Для более безопасной версии мы могли бы гарантировать, что она
T
действительно происходит изSelf<T>
:Обратите внимание, что
static_assert
внутренняя функция-член, вероятно, является единственным способом проверки, поскольку передаваемые типыstd::is_base_of
должны быть полными.источник
typename
в typedef. И поскольку это не уменьшает количество дублирующих элементов, я не думаю, что это жизнеспособная альтернатива.Foo
имени.Foo
вас нужно либо: (1) распространить T вверх на лист-потомок, либо (2) не забыть наследовать от SelfT много раз , или (3) принять, что все дети должны быть Базой ... пригодной для использования, но некрасивой.self
а неstatic
, это не проблема.Вы можете использовать макрос вместо обычного объявления класса, который сделает это за вас.
А затем используйте как
#define END_CLASS };
вероятно, улучшит читаемость.Вы также можете взять @ Paranaix's
Self
и использовать его (он начинает становиться действительно хакерским)источник
{
, поэтому}
он "зависает", что, вероятно, тоже не понравится текстовым редакторам.CLASS_WITH_SELF(foo) { … };
- а я думаю, что этого невозможно достичь.Self
имеет.У меня нет убедительных доказательств, но я думаю, что это невозможно. Следующее не удается - по той же причине, что и ваша попытка - и я думаю, что это максимум, что мы можем сделать:
По сути, это демонстрирует, что область, в которой мы хотим объявить наш typedef, просто не имеет доступа (будь то прямой или косвенный)
this
, и нет другого (независимого от компилятора) способа добраться до типа или имени класса.источник
decltype
- это неоцененный контекст, поэтому вызов функции-члена не проблема (это не будет предприниматься)struct S { int i; typedef decltype(i) Int; };
работает, даже еслиi
является нестатическим членом данных. Это работает, потому чтоdecltype
есть специальное исключение, когда простое имя не оценивается как выражение. Но я не могу придумать, как использовать эту возможность для ответа на этот вопрос.Что работает как в GCC, так и в clang, так это создание typedef, на который ссылается
this
, используяthis
в завершающем-возвращаемом-типе функции typedef. Поскольку это не объявление статической функции-члена, использованиеthis
допускается. Затем вы можете использовать этот typedef для определенияself
.К сожалению, строгое прочтение стандарта говорит о том, что даже это неверно. Что делает clang, так это проверка, которая
this
не используется в определении статической функции-члена. А здесь это действительно не так. GCC не возражает, еслиthis
он используется в конечном возвращаемом типе, независимо от типа функции, он позволяет это даже дляstatic
функций-членов. Однако на самом деле стандарт требует, чтобыthis
это не использовалось за пределами определения нестатической функции-члена (или инициализатора нестатического элемента данных). Intel понимает это правильно и отвергает это.При условии:
this
разрешено только в инициализаторах нестатических элементов данных и нестатических функциях-членах ([expr.prim.general] p5),this
их можно использовать ([over.call.func] p3),Я думаю, что могу окончательно сказать, что нет никакого способа реализовать
self
без включения каким-либо образом, где-то, имени типа.Изменить : в моих предыдущих рассуждениях есть изъян. «нестатические функции-члены могут вызываться только по неквалифицированному имени, даже в неоцененных контекстах, когда это можно использовать ([over.call.func] p3)» неверно. На самом деле он говорит
Внутри статической функции-члена
this
может не отображаться, но она все еще существует.Однако, согласно комментариям, внутри статической функции-члена преобразование
f()
в(*this).f()
не будет выполняться, и если это не выполняется, то [expr.call] p1 нарушается:так как не будет доступа для членов. Так что даже это не сработает.
источник
_self_fn_1()
"преобразовано" в(*this)._self_fn_1()
. Не уверен, что это делает его незаконным.X
в контексте, в которомthis
его можно использовать», поэтому я не думаю, что преобразование выполняется.auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);
?)const
перегрузок: это не проблема. 5.1p3 был специально изменен, чтобы также применяться к статическим функциям-членам, и говорит, что типthis
isFoo*
/Bar*
(безconst
), потому чтоconst
в объявлении нет_self_fn_2
.это не работает с типами шаблонов, так как
self_check
не вызывается, поэтомуstatic_assert
не оценивается.Мы можем сделать несколько приемов, чтобы заставить его работать для
template
s, но это требует незначительных затрат времени выполнения.struct
в вашем классе создается пустой размер 1 байт. Если ваш тип создан,self
проверяется.источник
template
вариантами поддержки классов.inline
. Это означает, что вам вообще не нужно писатьinline
. Так что, если вы писалиinline
перед каждым таким определением функции-члена класса на протяжении всей своей карьеры, вы можете остановиться сейчас;)Я тоже думаю, что это невозможно, вот еще одна неудачная, но ИМХО интересная попытка, которая позволяет избежать
this
-access:что не удается, потому что C ++ требует, чтобы вы
self_f
соответствовали классу, когда хотите получить его адрес :(источник
int T::*
указателем на переменную-член. Иint self_var; typedef decltype(&self_var) self_ptr
тоже не работает, это обычное делоint*
.Недавно я обнаружил, что
*this
это разрешено в инициализаторе фигурной скобки или равного . Описано в п. 5.1.1 ( из рабочего проекта n3337 ):Имея это в виду, следующий код:
проходит Даниэль Фрей
static_assert
.Live example
источник
test
хотя= this
, не так ли? И почему не простоusing self = Foo*;
test
к типу, мммFoo *
,!Если тип не должен быть тип члена вмещающего класса можно заменить использование
self
сdecltype(*this)
. Если вы используете его во многих местах своего кода, вы можете определить макросSELF
следующим образом:источник
self
ссылаться на непосредственно включающий класс, а не на внешний класс? Но я не так хорошо знаю php.Приведите мою версию. Лучше всего то, что он используется так же, как собственный класс. Однако это не работает для классов шаблонов.
источник
Основываясь на ответе hvd, я обнаружил, что единственное, чего не хватало, - это удаление ссылки, поэтому проверка std :: is_same не выполняется (b / c результирующий тип фактически является ссылкой на тип). Теперь этот макрос без параметров может делать всю работу. Рабочий пример ниже (я использую GCC 8.1.1).
источник
Я повторю очевидное решение «делать самому». Это краткая версия кода C ++ 11, которая работает как с простыми классами, так и с шаблонами классов:
Вы можете увидеть это в действии на ideone . Ниже приводится генезис, приведший к такому результату:
Это имеет очевидную проблему с копированием кода в другой класс и забыванием изменить XYZ, как здесь:
Мой первый подход был не очень оригинальным - я сделал такую функцию:
Это довольно долго, но, пожалуйста, будьте терпеливы. Это имеет то преимущество, что работает в C ++ 03 без него
decltype
, поскольку__self_check_helper
функция используется для определения типаthis
. Также нетstatic_assert
, ноsizeof()
вместо этого используется трюк. Вы могли бы сделать его намного короче для C ++ 0x. Теперь это не будет работать с шаблонами. Кроме того, есть небольшая проблема с макросом, не ожидающим точки с запятой в конце, при компиляции с педантичностью он будет жаловаться на лишнюю ненужную точку с запятой (или вы останетесь с макросом странного вида, не оканчивающимся точкой с запятой в телеXYZ
иABC
).Проверяя
Type
что передано,DECLARE_SELF
не является вариантом, поскольку это будет проверять толькоXYZ
класс (что нормально), не обращая вниманияABC
(который имеет ошибку). И тут меня осенило. Бесплатное решение без дополнительных хранилищ, работающее с шаблонами:Это просто делает статическое утверждение на уникальном значении перечисления (или, по крайней мере, уникальное, если вы не пишете весь свой код в одной строке), никаких уловок сравнения типов не используется, и оно работает как статическое утверждение, даже в шаблонах . И как бонус - теперь обязательная точка с запятой :).
Я хотел бы поблагодарить Yakk за хорошее вдохновение. Я бы не стал писать это, не увидев сначала его ответа.
Протестировано с VS 2008 и g ++ 4.6.3. Действительно, с помощью
XYZ
иABC
, например, он жалуется:Теперь, если мы сделаем ABC шаблоном:
Мы получим:
Сработала только проверка номера строки, поскольку проверка функции не была скомпилирована (как ожидалось).
С C ++ 0x (и без злого подчеркивания) вам понадобится всего:
Я считаю, что бит CStaticAssert, к сожалению, все еще требуется, поскольку он создает тип, который определяется типом в теле шаблона (я полагаю, с этим нельзя сделать то же самое
static_assert
). Преимуществом этого подхода по-прежнему является его нулевая стоимость.источник
static_assert
, не так ли? Кроме того, ваш полный код недействителен, потому что вы используете недопустимые (зарезервированные) идентификаторы.Я не знаю всего об этих дурацких шаблонах, как насчет чего-то сверхпростого:
Работа сделана, если только вы не переносите пару макросов. Вы даже можете использовать
CLASSNAME
для объявления своего конструктора (ов) (и, конечно же, деструктора).Живая демонстрация .
источник