Скрытые возможности C++?

Нет любви C++, когда речь идет о "скрытых особенностях" ряда вопросов? Подумал, что брошу его туда. Какие скрытые возможности C++?

@Devtron - Я видел несколько замечательных ошибок (например, неожиданное поведение), продаваемых как функции. Фактически, игровая индустрия на самом деле пытается сделать это в наши дни и называет это «эмерджентным геймплеем» (также проверьте «TK Surfing» от Psi-Ops, это была чистая ошибка, затем они оставили все как есть, и это было одним из лучшие особенности игры ИМХО)

Grant Peters 06.01.2010 18:11

@Laith J: Не так много людей прочитали 786-страничный стандарт ISO C++ от корки до корки, но я полагаю, что вы читали и сохранили все это, верно?

j_random_hacker 14.02.2010 22:20

@Laith, @j_random: См. Мой вопрос «Что такое шутка программиста, как ее распознать и каков правильный ответ» на stackoverflow.com/questions/1/you-have-been-link-rolled.

Roger Pate 26.02.2010 11:57

См. Сообщения meta.stackexchange.com/questions/56669/…, meta.stackexchange.com/questions/57226/… и связанные с ними мета-сообщения.

Roger Pate 16.07.2010 06:14
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
114
4
81 294
64

Ответы 64

Я не уверен насчет скрытого, но есть некоторые интересно'ухищрения', которые, вероятно, не очевидны после простого чтения спецификации.

Даже если он старше на 55 минут, его следует объединить / объединить с дубликатом в stackoverflow.com/questions/75538/hidden-features-of-c/… или просто удалить в пользу другого.

Roger Pate 26.02.2010 11:43

Пожалуйста ... ПОЖАЛУЙСТА, не используйте Устройство Даффа.

Billy ONeal 05.07.2010 00:48

C++ is a standard, there shouldn't be any hidden features...

C++ - это многопарадигмальный язык, вы можете поставить последние деньги на наличие скрытых функций. Один пример из многих: метапрограммирование шаблона. Никто в комитете по стандартам не намеревался создать полный по Тьюрингу подъязык, который будет выполняться во время компиляции.

Я нашел этот блог прекрасным ресурсом о секретах C++: Истины C++.

Здесь нет скрытых функций, но язык C++ очень мощный, и часто даже разработчики стандартов не могли представить, для чего можно использовать C++.

На самом деле, используя достаточно простую языковую конструкцию, вы можете написать что-нибудь очень мощное. Многие из таких вещей доступны на www.boost.org в качестве примеров (и http://www.boost.org/doc/libs/1_36_0/doc/html/lambda.html среди них).

Чтобы понять, как простая языковая конструкция может быть объединена с чем-то мощным, полезно прочитать "Шаблоны C++: полное руководство" Дэвида Вандевурда, Николая М. Йосаттиса и действительно волшебную книгу «Современный дизайн на C++ ...» Андрея Александреску.

И, наконец, С ++ сложно выучить, надо попробовать его залить;)

Есть много «неопределенного поведения». Вы можете узнать, как избежать чтения хороших книг и стандартов.

Как это особенность? Есть два разных способа рассматривать это как функцию, но я не вижу здесь никаких указаний на это. -1 за расплывчатость.

Roger Pate 26.02.2010 11:44

Или вы можете научиться принимать их, что хорошо.

Thomas Eding 23.02.2012 22:29

В C++ существует масса «хитрых» конструкций. Они идут от «простых» реализаций закрытые / финальные классы с использованием виртуального наследования. И переходите к довольно «сложным» конструкциям метапрограммирования, таким как MPL (руководство) Boost. Возможности для стрельбы себе в ногу безграничны, но если их контролировать (например, опытные программисты), они обеспечивают лучшую гибкость с точки зрения ремонтопригодности и производительности.

One example out of many: template metaprogramming. Nobody in the standards committee intended there to be a Turing-complete sublanguage that gets executed at compile-time.

Метапрограммирование шаблонов вряд ли является скрытой функцией. Это даже в библиотеке наддува. См. MPL. Но если «почти скрыто» достаточно, взгляните на увеличить библиотеки. Он содержит много полезных вещей, которые нелегко получить без поддержки сильной библиотеки.

Одним из примеров является библиотека boost.lambda, которая интересна, поскольку C++ не имеет лямбда-функций в текущем стандарте.

Другой пример - Локи, который «широко использует метапрограммирование шаблонов C++ и реализует несколько часто используемых инструментов: список типов, функтор, синглтон, интеллектуальный указатель, фабрику объектов, посетитель и мультиметоды». [Википедия]

Метапрограммирование шаблонов больше не скрывается, потому что оно было очень полезным. Однако это скрыто в том, что эта функция не разработана на C++, а появилась случайно.

Konrad Rudolph 17.09.2008 13:30

О времени жизни временных файлов, привязанных к константным ссылкам, мало кто знает. Или, по крайней мере, это моя любимая часть знаний о C++, о которой большинство людей не знает.

const MyClass& x = MyClass(); // temporary exists as long as x is in scope

Вы можете уточнить? Как это ты просто дразнишь;)

Joseph Garvin 22.06.2009 01:55

ScopeGuard (ddj.com/cpp/184403758) - отличный пример, который использует эту функцию.

MSN 22.06.2009 20:48

Я с Джозефом Гарвином. Пожалуйста, просветите нас.

Peter Mortensen 07.07.2009 01:04

Я только что сделал в комментариях. Кроме того, это естественное следствие использования константного ссылочного параметра.

MSN 11.07.2009 03:11

Я прочитал ответ и ссылку на ScopeGuard и до сих пор не понимаю, что это значит.

Oak 01.02.2010 14:36

@ Дуб: stackoverflow.com/questions/256724/…

BlueRaja - Danny Pflughoeft 29.05.2010 02:17

Чем это отличается от: const MyClass x; ?

Ferruccio 19.11.2010 02:23

@Ferruccio, в этом случае он идентичен, но вы можете использовать производный класс, и его время жизни по-прежнему будет расширено до области действия ссылки const.

MSN 19.11.2010 02:58

Оператор массива ассоциативен.

A [8] является синонимом * (A + 8). Поскольку сложение ассоциативно, его можно переписать как * (8 + A), что является синонимом ..... 8 [A]

Вы не сказали, что полезно ... :-)

На самом деле, используя этот трюк, вы действительно должны обращать внимание на то, какой тип вы используете. A [8] на самом деле является 8-м A, тогда как 8 [A] - это целое число Ath, начинающееся с адреса 8. Если A - это байт, у вас есть ошибка.

Vincent Robert 17.09.2008 04:03

Вы имеете в виду "коммутативный", когда говорите "ассоциативный"?

DarenW 17.09.2008 04:15

Винсент, ты ошибаешься. Тип A вообще не имеет значения. Например, если бы A был char*, код все равно был бы действителен.

Konrad Rudolph 17.09.2008 13:33

Помните, что A должен быть указателем, а не оператором перегрузки класса [].

David Rodríguez - dribeas 03.01.2009 18:17

Винсент, здесь должен быть один интегральный тип и один тип указателя, и ни C, ни C++ не заботятся о том, какой из них идет первым.

David Thornley 08.01.2009 00:10

A [8] соответствует *(A + 8). 8 [A] соответствует *(8 + A). Они одинаковые.

Marlon 04.02.2011 05:16

Большинство разработчиков C++ игнорируют возможности метапрограммирования шаблонов. Проверьте Loki Libary. Он реализует несколько расширенных инструментов, таких как список типов, функтор, синглтон, интеллектуальный указатель, фабрика объектов, посетитель и мультиметоды, широко использующие метапрограммирование шаблонов (из википедия). По большей части вы можете рассматривать их как «скрытую» функцию C++.

Мы не игнорируем это, мы избегаем этого ....

Jimmy J 23.03.2009 23:26

Дубликат метапрограммирования шаблона из stackoverflow.com/questions/75538/hidden-features-of-c/…. Эти примеры объединены в ответ, поэтому дубликат можно удалить.

Roger Pate 26.02.2010 11:35

Я согласен с большинством постов там: C++ - это язык с множеством парадигм, поэтому «скрытые» функции, которые вы найдете (кроме «неопределенного поведения», которого вам следует избегать любой ценой), - это умное использование возможностей.

Большинство из этих возможностей не являются встроенными функциями языка, а основаны на библиотеках.

Наиболее важным является RAII, который часто игнорируется в течение многих лет разработчиками C++ из мира C. Перегрузка оператора часто неправильно понимается функцией, которая обеспечивает как поведение, подобное массиву (оператор индекса), операции, подобные указателям (интеллектуальные указатели), так и операции, подобные встроенным (умножение матриц.

Использование исключение часто затруднено, но при некоторой работе можно получить действительно надежный код с помощью спецификаций безопасность исключений (включая код, который не будет терпеть неудачу, или который будет иметь функции, подобные фиксации, которые будут успешными, или вернуться к своему исходное состояние).

Самая известная из «скрытых» функций C++ - это метапрограммирование шаблона, поскольку она позволяет вам частично (или полностью) выполнять вашу программу во время компиляции, а не во время выполнения. Однако это сложно, и вы должны хорошо разбираться в шаблонах, прежде чем пробовать это делать.

Другие используют множественную парадигму для создания «способов программирования» вне предка C++, то есть C.

Используя функторы, вы можете моделировать функции с дополнительной безопасностью типов и с отслеживанием состояния. Используя шаблон команда, вы можете отложить выполнение кода. Большинство других шаблоны проектирования могут быть легко и эффективно реализованы на C++ для создания альтернативных стилей кодирования, которые не должны входить в список «официальных парадигм C++».

Используя шаблоны, вы можете создать код, который будет работать с большинством типов, включая не тот, который вы думали вначале. Вы также можете повысить безопасность типов (например, автоматический тип malloc / realloc / free). Возможности объектов C++ действительно мощные (и, следовательно, опасны при неосторожном использовании), но даже динамический полиморфизм имеет свою статическую версию в C++: CRTP.

Я обнаружил, что большинство книг типа "Эффективный C++" от Скотта Мейерса или книг типа "Исключительный C++" от Херба Саттера одновременно легко читаются и представляют собой кладезь информации об известных и менее известных особенностях C++.

Среди моих предпочтений есть тот, который должен вызвать ужас у любого Java-программиста: в C++ это наиболее объектно-ориентированный способ добавить функцию к объекту - использовать функцию, не являющуюся членом, не являющуюся другом, вместо функции-члена (т.е. метод класса), потому что:

  • В C++ интерфейс класса - это как его функции-члены, так и функции, не являющиеся членами, в одном и том же пространстве имен.

  • не являющиеся друзьями функции, не являющиеся членами, не имеют привилегированного доступа к внутреннему классу. Таким образом, использование функции-члена вместо не-члена, не являющегося другом, ослабит инкапсуляцию класса.

Это не перестает удивлять даже опытных разработчиков.

(Источник: среди прочего, онлайн-гуру недели Херба Саттера № 84: http://www.gotw.ca/gotw/084.htm)

+1 очень обстоятельный ответ. он неполный по очевидным причинам (иначе больше не было бы «скрытых функций»!): p в первом пункте в конце ответа вы упомянули члены интерфейса класса. Вы имеете в виду, что ".. это и его функции-члены, и функции, не являющиеся членами друг"?

wilhelmtell 04.10.2008 02:31

то, о чем вы говорите с 1, должно быть поиском koenig, верно?

Özgür 30.10.2008 03:43

@wilhelmtell: Нет-нет-нет ... :-p ... Я ДЕЙСТВИТЕЛЬНО имею в виду "его функции-члены и НЕЧЛЕННЫЕ функции, не являющиеся членами" .... Поиск Кенига гарантирует, что эти функции будут рассмотрены раньше, чем другие " вне "функций в поиске символов

paercebal 31.10.2008 00:45

@Comptrol: Вы почти правы, это последствие поиска Кеонига.

paercebal 31.10.2008 00:45

Отличный пост, и +1 особенно к последней части, о чем мало кто догадывается. Я бы, наверное, добавил библиотеку Boost как «скрытую функцию». Я в значительной степени считаю это стандартной библиотекой, которую должен был иметь C++. ;)

jalf 19.11.2008 20:04

Ооо, вместо этого я могу составить список ненависти к животным:

  • Деструкторы должны быть виртуальными, если вы собираетесь использовать их полиморфно.
  • Иногда члены инициализируются по умолчанию, иногда нет.
  • Локальные классы нельзя использовать в качестве параметров шаблона (что делает их менее полезными)
  • спецификаторы исключений: выглядят полезными, но не
  • перегрузки функций скрывают функции базового класса с разными сигнатурами.
  • нет полезной стандартизации по интернационализации (переносимая стандартная широкая кодировка, кто-нибудь? Нам придется подождать до C++ 0x)

С положительной стороны

  • скрытая особенность: функциональные блоки try. К сожалению, я не нашел ему применения. Да, я знаю, почему они добавили его, но вам нужно повторно добавить конструктор, что делает его бессмысленным.
  • Стоит внимательно изучить гарантии STL относительно допустимости итератора после модификации контейнера, что может позволить вам сделать несколько более приятных циклов.
  • Boost - это вряд ли секрет, но стоит использовать.
  • Оптимизация возвращаемого значения (неочевидно, но специально разрешено стандартом)
  • Функторы, известные как функциональные объекты, также известные как operator (). Это широко используется STL. на самом деле не секрет, но это отличный побочный эффект перегрузки операторов и шаблонов.

домашняя ненависть: нет определенного ABI для приложений C++, в отличие от приложений C, которые все используют, потому что каждый язык может гарантировать вызов функции C, никто не может сделать то же самое для C++.

gbjbaanb 25.09.2008 12:52

Деструкторы должны быть виртуальными только в том случае, если вы намереваетесь уничтожать полиморфно, что немного отличается от первого пункта.

David Rodríguez - dribeas 03.01.2009 16:25

В C++ 0x локальные типы могут использоваться в качестве параметров шаблона.

tstenner 23.04.2009 17:21

В C++ 0x деструкторы будут виртуальными, если у объекта есть какие-либо виртуальные функции (например, vtable).

Macke 15.10.2009 20:12

не забывайте NRVO, и, конечно, разрешена любая оптимизация, если она не меняет вывод программы.

jk. 26.02.2010 12:10

Спецификации исключений C++ 0x устарели по сравнению со стандартом.

user90843 04.10.2011 20:58

Одна языковая функция, которую я считаю несколько скрытой, потому что я никогда не слышал о ней за все время учебы в школе, - это псевдоним пространства имен. Это не было доведено до моего сведения, пока я не встретил его примеры в документации по усилению. Конечно, теперь, когда я знаю об этом, вы можете найти его в любом стандартном справочнике по C++.

namespace fs = boost::filesystem;

fs::path myPath( strPath, fs::native );

Думаю, это полезно, если вы не хотите использовать using.

Siqi Lin 20.08.2010 06:44

Это также полезно как способ переключения между реализациями, будь то выбор, скажем, потокобезопасный или небезопасный, или версия 1 против 2.

Tony Delroy 24.01.2011 13:22

Это особенно полезно, если вы работаете над очень большим проектом с большими иерархиями пространств имен и не хотите, чтобы ваши заголовки вызывали загрязнение пространства имен (и вы хотите, чтобы объявления ваших переменных были удобочитаемыми).

Brandon Bohrer 17.03.2011 03:40
  • указатели на методы класса
  • Ключевое слово "typename"

Вы можете без ошибок помещать URI в исходный код C++. Например:

void foo() {
    http://stackoverflow.com/
    int bar = 4;

    ...
}

Но я подозреваю, что только по одному на каждую функцию? :)

Constantin 05.10.2008 21:40

Это ошибка? Что он должен делать? Я попробовал, и он действительно скомпилировался без ошибок; Я получил предупреждение: предупреждение C4102: 'http': метка без ссылки

jpoh 21.10.2008 09:56

Только не пытайтесь добавить более одного на исходный файл: P

X-Istence 26.10.2008 18:21

@jpoh: http, за которым следует двоеточие, становится «меткой», которую вы позже используете в операторе goto. вы получаете это предупреждение от своего компилятора, потому что оно не используется ни в одном операторе goto в приведенном выше примере.

utku_karatas 04.11.2008 20:07

Вы можете добавить более одного, если у них разные протоколы! ftp.microsoft.com gopher: //aerv.nl/1 и так далее ...

Daniel Earwicker 28.03.2009 00:28

@Pavel: идентификатор, за которым следует двоеточие, является меткой (для использования с goto, который есть в C++). Все, что следует за двумя косыми чертами, является комментарием. Следовательно, с http://stackoverflow.comhttp является меткой (теоретически можно написать goto http;), а //stackoverflow.com - просто комментарием в конце строки. Оба они являются законным C++, поэтому конструкция компилируется. Конечно, он не делает ничего неопределенно полезного.

David Thornley 29.05.2010 01:55

Если у вас включены предупреждения об ошибках, он не будет собираться, по крайней мере, в VS2005

Chris Huang-Leaver 07.01.2011 15:21

@Chris: вы можете исправить это, добавив goto http; ;-)

Tony Delroy 24.01.2011 13:15

К сожалению, goto http; на самом деле не следует по URL. :(

Yakov Galka 02.08.2011 21:01

@titaniumdecoy Подождите, Java поддерживает goto?

Mateen Ulhaq 27.09.2011 04:17

@muntoo, вообще-то да, вроде того. stackoverflow.com/a/2545160/65977

MatrixFrog 14.12.2011 08:18

Указатель арифметики.

На самом деле это функция C, но я заметил, что немногие люди, использующие C / C++, действительно знают, что она вообще существует. Я считаю, что эта особенность языка C действительно свидетельствует о гениальности и видении его изобретателя.

Короче говоря, арифметика указателей позволяет компилятору выполнять [n] как * (a + n) для любого типа a. В качестве примечания, поскольку '+' коммутативен, a [n], конечно, эквивалентно n [a].

Избавляемся от форвардных объявлений:

struct global
{
     void main()
     {
           a = 1;
           b();
     }
     int a;
     void b(){}
}
singleton;

Написание операторов switch с помощью операторов?::

string result = 
    a==0 ? "zero" :
    a==1 ? "one" :
    a==2 ? "two" :
    0;

Делаем все в одной строке:

void a();
int b();
float c = (a(),b(),1.0f);

Обнуление структур без memset:

FStruct s = {0};

Нормализация / обертывание значений угла и времени:

int angle = (short)((+180+30)*65536/360) * 360/65536; //==-150

Назначение референсов:

struct ref
{
   int& r;
   ref(int& r):r(r){}
};
int b;
ref a(b);
int c;
*(int**)&a = &c;

FStruct s = {}; еще короче.

Constantin 05.10.2008 21:43

В последнем примере было бы проще: a (); б (); float c = 1.0f;

Zifre 16.04.2009 03:48

@Zifre: фокус как раз в последней строке float c = (a (), b (), 1.0f); - помещать все, что закрыто полуклоном, в одну строку - это вообще не уловка. Но будьте осторожны: int d = (a (), b (), 1.0f); получит значение b!

fmuecke 19.11.2009 15:57

Этот синтаксис «float c = (a (), b (), 1.0f);» полезен для акцентирования ассигнования-операции (присвоение «с»). Операции присваивания важны в программировании, потому что они с меньшей вероятностью станут устаревшими IMO. Не знаю почему, это может быть связано с функциональным программированием, когда состояние программы переназначается каждый кадр. PS. И нет, «int d = (11,22,1.0f)» будет равно «1». Протестировано минуту назад с VS2008.

AareP 19.11.2009 20:29

+1 Разве вы не должны быть вызовmain? Я бы предложил global().main(); и просто забыл о синглтоне (вы можете просто работать с временным, что продлевает срок его службы)

sehe 04.12.2011 05:40

Я сомневаюсь, что присвоение ссылок переносимо. Тем не менее, мне нравится, что структура отклоняет предварительные объявления.

Thomas Eding 23.02.2012 22:13

Приятной особенностью, которая используется нечасто, является блок try-catch для всей функции:

int Function()
try
{
   // do something here
   return 42;
}
catch(...)
{
   return -1;
}

Основное использование будет заключаться в преобразовании исключения в другой класс исключения и повторном вызове или в преобразовании между исключениями и обработкой кода ошибки на основе возврата.

Я не думаю, что вы можете return из блока catch функции Try, только перебросить.

Constantin 05.10.2008 21:45

Я просто попытался скомпилировать вышеуказанное, и это не дало никаких предупреждений. Я думаю, что приведенный выше пример работает.

vividos 22.10.2008 11:45

Возврат запрещен только для строителей. Блок try функции конструктора будет отлавливать ошибки при инициализации базы и членов (единственный случай, когда блок try функции делает что-то иное, чем просто попытка внутри функции); отказ от повторного броска приведет к получению неполного объекта.

puetzk 18.12.2008 07:42

Да. Это очень полезно. Я написал макросы BEGIN_COM_METHOD и END_COM_METHOD для перехвата исключений и возврата HRESULTS, чтобы исключения не просачивались из класса, реализующего COM-интерфейс. Это сработало.

Scott Langham 18.02.2009 15:47

@puetzk Я полагаю, было бы очень полезно вернуть "DefaultObject" из конструктора, который потерпел неудачу. Как если бы конструктор вышел из строя. Вы все еще можете вернуть пустой объект. Как будто вы хотите загрузить изображение. При ошибке загрузки возвращается изображение по умолчанию.

Loïc Faure-Lacroix 17.03.2009 04:19

Это довольно круто. Я понятия не имел, что вы можете это сделать, и людям не кажется, что он даже компилируется.

Nick Bedford 26.03.2010 03:43

Как указывает @puetzk, это единственный способ обрабатывать исключения, создаваемые что-либо в список инициализаторов конструктора, такие как конструкторы базовых классов или конструкторы членов данных.

anton.burger 23.06.2010 14:56

@kizzx: это экономит вам одну открытую и одну закрывающую скобу.

Yakov Galka 31.12.2010 19:48

@kizzx: он также сохраняет дальнейшие отступы тела функции ... хорошо, если вам лень менять отступ или работать на экране фиксированной ширины и вы хотите использовать больше.

Tony Delroy 24.01.2011 13:27

Скрытые возможности:

  1. Чистые виртуальные функции могут иметь реализацию. Типичный пример, чистый виртуальный деструктор.
  2. Если функция генерирует исключение, не указанное в ее спецификациях исключения, но функция имеет std::bad_exception в своей спецификации исключения, исключение преобразуется в std::bad_exception и генерируется автоматически. Таким образом вы хотя бы узнаете, что был брошен bad_exception. Узнать больше здесь.

  3. функциональные блоки try

  4. Ключевое слово template для устранения неоднозначности определений типов в шаблоне класса. Если имя специализации шаблона элемента появляется после оператора ., -> или ::, и это имя имеет явно определенные параметры шаблона, добавьте к имени шаблона элемента префикс ключевого слова template. Узнать больше здесь.

  5. Значения по умолчанию для параметров функции могут быть изменены во время выполнения. Узнать больше здесь.

  6. A[i] работает так же хорошо, как i[A]

  7. Временные экземпляры класса можно изменять! Неконстантная функция-член может быть вызвана для временного объекта. Например:

    struct Bar {
      void modify() {}
    }
    int main (void) {
      Bar().modify();   /* non-const function invoked on a temporary. */
    }
    

    Узнать больше здесь.

  8. Если два разных типа присутствуют до и после : в тернарном (?:) операторном выражении, то результирующий тип выражения является наиболее общим из двух. Например:

    void foo (int) {}
    void foo (double) {}
    struct X {
      X (double d = 0.0) {}
    };
    void foo (X) {} 
    
    int main(void) {
      int i = 1;
      foo(i ? 0 : 0.0); // calls foo(double)
      X x;
      foo(i ? 0.0 : x);  // calls foo(X)
    }
    

P Папа: A [i] == * (A + i) == * (i + A) == i [A]

abelenky 08.01.2009 01:08

Я получаю коммутацию, просто это означает, что [] не имеет собственного семантического значения и просто эквивалентен замене в макро-стиле, где «x [y]» заменяется на «(* ((x) + (y ))) ". Совсем не то, что я ожидал. Интересно, почему это так определяется.

P Daddy 08.01.2009 06:07

Обратная совместимость с C

jmucchiello 19.01.2009 11:05

Что касается вашего первого пункта: есть один частный случай, когда вы иметь реализуете чистую виртуальную функцию: чистые виртуальные деструкторы.

Frerich Raabe 30.10.2009 17:56

Инициализация массива в конструкторе. Например, в классе, если у нас есть массив int как:

class clName
{
  clName();
  int a[10];
};

Мы можем инициализировать все элементы в массиве по умолчанию (здесь все элементы массива равны нулю) в конструкторе как:

clName::clName() : a()
{
}

Вы можете сделать это с любым массивом где угодно.

Potatoswatter 26.02.2010 12:38

@Potatoswatter: сложнее, чем кажется, из-за самого неприятного синтаксического анализа. Я не могу придумать, где еще это можно было бы сделать, кроме может быть возвращаемого значения

Mooing Duck 19.10.2011 03:23

Если тип массива является типом класса, тогда это не нужно, верно?

Thomas Eding 23.02.2012 21:27

map::operator[] создает запись, если ключ отсутствует, и возвращает ссылку на созданное по умолчанию значение записи. Итак, вы можете написать:

map<int, string> m;
string& s = m[42]; // no need for map::find()
if (s.empty()) { // assuming we never store empty values in m
  s.assign(...);
}
cout << s;

Я поражен тем, сколько программистов на C++ этого не знают.

С другой стороны, вы не можете использовать operator [] на карте const.

David Rodríguez - dribeas 03.01.2009 18:22

+1 за Ника, люди могут сойти с ума, если не знают о .find().

LiraNuna 10.09.2009 02:19

или «const map::operator[] выдает сообщения об ошибках»

just somebody 27.12.2009 23:54

Это не особенность языка, это особенность стандартной библиотеки шаблонов. Это также довольно очевидно, поскольку operator [] возвращает действительную ссылку.

Ramon Zarazua B. 26.02.2010 12:01

Он по-прежнему кусает людей: «map <int, data *> m ​​-> if (m [i] == 0) return; / * Not found * /» - коварная утечка памяти.

Lars 16.03.2010 02:25

Мне пришлось какое-то время использовать карты на C#, где карты не ведут себя таким образом, чтобы понять, что это особенность. Я думал, что меня это раздражает больше, чем я им пользовался, но, похоже, я ошибался. Мне не хватает этого на C#.

sbi 20.09.2010 11:47

Когда-то, еще не зная много о контейнерах C++, я уже много использовал ассоциативные массивы PHP. На этом фоне это было совершенно противоположным сюрпризом. На самом деле для меня было довольно удивительно, что нет V operator[] (K) const.

Sebastian Mach 21.07.2011 16:12

Помещение функций или переменных в безымянное пространство имен осуждает использование static, чтобы ограничить их областью файлов.

"осуждает" - сильный термин ...

Potatoswatter 26.02.2010 12:37

@Potato: Старый комментарий, я знаю, но в стандарте говорится, что использование static в области пространства имен устарело, с предпочтением для безымянных пространств имен.

GManNickG 21.04.2010 09:27

@GMan: нет проблем, я не думаю, что ТАК страницы действительно "умирают". По обе стороны истории, static в глобальном масштабе никоим образом не является устаревшим. (Для справки: C++ 03 §D.2)

Potatoswatter 21.04.2010 09:39

А, при более внимательном чтении: «Имя, объявленное в глобальном пространстве имен, имеет глобальную область действия (также называемую глобальной областью)». Это действительно так?

Potatoswatter 21.04.2010 09:46

@ Картофель: Ага. :) static следует использовать только внутри класса или функции.

GManNickG 21.04.2010 10:02

Большинство программистов на C++ знакомы с тернарным оператором:

x = (y < 0) ? 10 : 20;

Однако они не понимают, что его можно использовать как lvalue:

(a == 0 ? a : b) = 1;

что является сокращением для

if (a == 0)
    a = 1;
else
    b = 1;

Используйте с осторожностью :-)

Очень интересно. Я вижу, что это создает нечитаемый код.

Jason Baker 20.11.2008 05:59

Вы должны переместить закрывающую скобку: «(a == 0? A: b) = 1», иначе она компилируется как «a == 0? A: (b = 1)». Обратите внимание, что скобки вокруг "a == 0" не обязательны.

P Daddy 07.01.2009 23:29

Ой. (а == 0? а: б) = (у <0? 10: 20);

Jasper Bekkers 11.01.2009 07:12

Странно, что этого нет в C. Такие вещи, как однострочные комментарии (//), были перенесены обратно, и это определенно не могло сломать какой-либо код. Думаю, это тоже может быть полезно. Ну что ж.

Andrew Keeton 23.06.2009 00:40

(b? trueCount: falseCount) ++

Pavel Radzivilovsky 26.05.2010 15:25

Допускает ли это какой-либо другой язык?

Casebash 15.07.2010 09:33

Обычно я воздерживаюсь от использования тернарного оператора, потому что он затрудняет чтение кода.

Siqi Lin 20.08.2010 06:42

Не знаю, специфичен ли он для GCC, но я был удивлен, обнаружив, что это тоже сработало: (value ? function1 : function2)().

Chris Burt-Brown 13.10.2010 16:34

@Chris Burt-Brown: Нет, это должно работать везде, если они имеют один и тот же тип (т.е. нет аргументов по умолчанию). function1 и function2 неявно преобразуются в указатели функций, а результат неявно преобразуется обратно.

MSalters 12.11.2010 18:49

Считываем файл в вектор строк:

 vector<string> V;
 copy(istream_iterator<string>(cin), istream_iterator<string>(),
     back_inserter(V));

istream_iterator

Или: vector <string> V ((istream_iterator <string> (cin)), istream_iterator <string>);

UncleBens 10.09.2009 02:26

вы имеете в виду vector<string> V((istream_iterator<string>(cin)), istream_iterator<string>()); - отсутствие скобок после второго параметра

knittl 23.01.2010 23:14

На самом деле это не скрытая функция C++. Больше функции STL. STL! = Язык

Nick Bedford 26.03.2010 03:47

Pointer arithmetics.

Программисты на C++ предпочитают избегать указателей из-за возможных ошибок.

Но самый крутой C++, который я когда-либо видел? Аналоговые литералы.

Избегаем указателей из-за ошибок? По сути, указатели - это все, что связано с динамическим кодированием на C++!

Nick Bedford 26.03.2010 03:36

Аналоговые литералы отлично подходят для запутанных записей конкурса C++, особенно типа ASCII-art.

Synetech 05.02.2011 08:15

Довольно скрытая особенность заключается в том, что вы можете определять переменные внутри условия if, и его область действия будет охватывать только if и его блоки else:

if (int * p = getPointer()) {
    // do something
}

Некоторые макросы используют это, например, чтобы обеспечить некоторую "заблокированную" область видимости, например:

struct MutexLocker { 
    MutexLocker(Mutex&);
    ~MutexLocker(); 
    operator bool() const { return false; } 
private:
    Mutex &m;
};

#define locked(mutex) if (MutexLocker const& lock = MutexLocker(mutex)) {} else 

void someCriticalPath() {
    locked(myLocker) { /* ... */ }
}

Также BOOST_FOREACH использует его под капотом. Чтобы завершить это, это возможно не только в if, но и в переключателе:

switch(int value = getIt()) {
    // ...
}

и в цикле while:

while(SomeThing t = getSomeThing()) {
    // ...
}

(а также в состоянии for). Но я не уверен, насколько они полезны :)

Аккуратный! Я никогда не знал, что вы сможете это сделать ... это избавило бы (и сэкономит) некоторые хлопоты при написании кода с возвращаемыми значениями ошибок. Есть ли способ сохранить в этой форме условное выражение вместо! = 0? if ((int r = func ()) <0) похоже не работает ...

puetzk 18.12.2008 07:58

puetzk, нет, нет. но рад, что тебе понравилось :)

Johannes Schaub - litb 11.01.2009 06:58

На самом деле моему другу пришлось использовать эту первую функцию, чтобы точно создать очень сложный макрос, который был необходим в существующей системе.

Kamil Szot 10.09.2009 02:37

+1 Наслаждайтесь этой ссылкой: ddj.com/cpp/184401728

fnieto - Fernando Nieto 06.11.2009 02:25

На самом деле это скрыто только для людей, которые никогда серьезно не писали код C. Обычно очень фиксирует возвращаемые значения в таких переменных.

Frerich Raabe 20.11.2009 19:51

@Frerich, это вообще невозможно в коде C. Я думаю, вы думаете о if ((a = f()) == b) ..., но этот ответ фактически объявляет переменную в условии.

Johannes Schaub - litb 20.11.2009 20:17

Это не сильно отличается от for (int i=foo(); i<N; ++i). Срок службы i - до выхода из цикла. Итератор очень часто определяется внутри скобок.

AngryWhenHungry 18.06.2010 13:07

@Angry это совсем другое, потому что объявление переменной сразу проверяется на его логическое значение. Также есть сопоставление с циклами for, которое выглядит как for(...; int i = foo(); ) ...;. Оно будет проходить через тело, пока i истинно, инициализируя его каждый раз заново. Цикл, который вы показываете, просто демонстрирует объявление переменной, но не объявление переменной, которое одновременно действует как условие :)

Johannes Schaub - litb 18.06.2010 14:24

Очень хорошо, за исключением того, что вы не упомянули, что предполагаемое использование этой функции было для динамического приведения указателей, я считаю.

mmocny 05.03.2011 19:03

От Истины C++.

Определение функций с идентичными подписями в одной и той же области, поэтому это допустимо:

template<class T> // (a) a base template
void f(T) {
  std::cout << "f(T)\n";
}

template<>
void f<>(int*) { // (b) an explicit specialization
  std::cout << "f(int *) specilization\n";
}

template<class T> // (c) another, overloads (a)
void f(T*) {
  std::cout << "f(T *)\n";
}

template<>
void f<>(int*) { // (d) another identical explicit specialization
  std::cout << "f(int *) another specilization\n";
}

+1 для неясности, хотя вы сами скрываете вещи, опуская тот факт, что приведенный выше код требует еще 2 деклараций шаблонов функций (1 в начале, 1 между ними) для компиляции.

j_random_hacker 22.01.2009 12:28

Если оператор delete () принимает аргумент размера в дополнение к * void, это означает, что он будет, в значительной степени, базовым классом. Этот аргумент размера делает возможной проверку размера типов, чтобы уничтожить правильный. Вот что об этом сообщает Стивен Дьюхерст:

Notice also that we've employed a two-argument version of operator delete rather than the usual one-argument version. This two-argument version is another "usual" version of member operator delete often employed by base classes that expect derived classes to inherit their operator delete implementation. The second argument will contain the size of the object being deleted—information that is often useful in implementing custom memory management.

Одна из самых интересных грамматик любого языка программирования.

Три из этих вещей принадлежат друг другу, а две совершенно разные ...

SomeType t = u;
SomeType t(u);
SomeType t();
SomeType t;
SomeType t(SomeType(u));

Все, кроме третьего и пятого, определяют объект SomeType в стеке и инициализируют его (с помощью u в первых двух случаях и конструктором по умолчанию в четвертом. Третий объявляет функцию, которая не принимает параметров и возвращает SomeType. Пятый аналогичным образом объявляет функцию, которая принимает один параметр по значению типа SomeType с именем u.

есть ли разница между 1-м и 2-м? хотя я знаю, что это обе инициализации.

Özgür 07.01.2009 23:53

Comptrol: Я так не думаю. Оба в конечном итоге вызовут конструктор копирования, хотя первый ВЫГЛЯДИТ как оператор присваивания, на самом деле это конструктор копирования.

abelenky 08.01.2009 01:05

Если u является типом, отличным от SomeType, то первый вызовет сначала конструктор преобразования, а затем конструктор копирования, тогда как второй вызовет только конструктор преобразования.

Eclipse 08.01.2009 02:32

1-й - неявный вызов конструктора, 2-й - явный вызов. Посмотрите на следующий код, чтобы увидеть разницу: #include <iostream> class sss {public: explicit sss (int) {std :: cout << "int" << std :: endl; }; sss (двойной) {std :: cout << "double" << std :: endl; }; }; int main () {sss ddd (7); // вызывает конструктор int sss xxx = 7; // вызывает двойной конструктор return 0; }

Kirill V. Lyadvinsky 24.06.2009 00:04

Верно - первая строка не будет работать, если конструктор объявлен явно.

Eclipse 24.06.2009 00:43

Иначе известный как: Самый неприятный синтаксический анализ. «Исправлено» в C++ 0x с новым синтаксисом: SomeType t {SomeType {u}}

mmocny 05.03.2011 19:32

Мало что известно о том, что союзы тоже могут быть шаблонами:

template<typename From, typename To>
union union_cast {
    From from;
    To   to;

    union_cast(From from)
        :from(from) { }

    To getTo() const { return to; }
};

И у них тоже могут быть конструкторы и функции-члены. Ничего общего с наследованием (включая виртуальные функции).

Интересно! Итак, вы должны инициализировать всех участников? Соответствует ли он обычному порядку структуры, подразумевая, что последний член будет инициализирован «поверх» более ранних членов?

j_random_hacker 22.01.2009 12:31

j_random_hacker ах, да это чушь. хороший улов. Я написал это как структуру. подожди, я исправлю это

Johannes Schaub - litb 22.01.2009 14:22

Разве это не вызывает неопределенное поведение?

Greg Bacon 30.01.2010 17:11

@gbacon, да, он вызывает неопределенное поведение, если From и To установлены и используются соответственно. Однако такое объединение можно использовать с определенным поведением (при этом To является массивом символов без знака или структурой, разделяющей начальную последовательность с From). Даже если вы используете его неопределенным образом, он все равно может быть полезен для низкоуровневой работы. В любом случае, это всего лишь один пример шаблона объединения - шаблонное объединение может быть использовано и в других целях.

Johannes Schaub - litb 30.01.2010 18:32

Это интересно, никогда не знал, что у объединений на самом деле есть эти функции из классов и структур!

Ramon Zarazua B. 26.02.2010 11:58

Осторожнее с конструктором. Обратите внимание, что вам необходимо создать только первый элемент, и это разрешено только в C++ 0x. В соответствии с текущим стандартом вы должны придерживаться тривиально конструируемых типов. И никаких деструкторов.

Potatoswatter 26.02.2010 12:18

Так в основном вы это делаете? *(B *)&a

Nick Bedford 26.03.2010 03:39

@ JohannesSchaub-litb: также можно создавать шаблоны битовых полей: stackoverflow.com/a/8309592/85371

sehe 04.12.2011 05:06

Особого внимания требует определение обычных функций друзей в шаблонах классов:

template <typename T> 
class Creator { 
    friend void appear() {  // a new function ::appear(), but it doesn't 
        …                   // exist until Creator is instantiated 
    } 
};
Creator<void> miracle;  // ::appear() is created at this point 
Creator<double> oops;   // ERROR: ::appear() is created a second time! 

В этом примере два разных экземпляра создают два идентичных определения - прямое нарушение ODR

Поэтому мы должны убедиться, что параметры шаблона шаблона класса присутствуют в типе любой дружественной функции, определенной в этом шаблоне (если мы не хотим предотвратить создание более одного экземпляра шаблона класса в конкретном файле, но это маловероятно). Давайте применим это к варианту нашего предыдущего примера:

template <typename T> 
class Creator { 
    friend void feed(Creator<T>*){  // every T generates a different 
        …                           // function ::feed() 
    } 
}; 

Creator<void> one;     // generates ::feed(Creator<void>*) 
Creator<double> two;   // generates ::feed(Creator<double>*) 

Отказ от ответственности: я вставил этот раздел из Шаблоны C++: полное руководство / Раздел 8.4

Опасный секрет

Fred* f = new(ram) Fred(); http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.10
f->~Fred();

Мой любимый секрет, который я редко использую:

class A
{
};

struct B
{
  A a;
  operator A&() { return a; }
};

void func(A a) { }

int main()
{
  A a, c;
  B b;
  a=c;
  func(b); //yeah baby
  a=b; //gotta love this
}

Умм, неприятно это говорить, но на самом деле это не очень скрытый «оператор приведения типов». Об этом, наверное, знает любой, кто хоть раз видел перегрузки операторов.

Nick Bedford 26.03.2010 03:49

Ведущие программисты на моей работе не знали об этом. Только один человек, которого я встречал, знал об этом. Также я видел учебники, в которых это не упоминается, включая мой любимый учебник cplusplus.com/doc/tutorial

user34537 26.03.2010 21:15

class Empty {};

namespace std {
  // #1 specializing from std namespace is okay under certain circumstances
  template<>
  void swap<Empty>(Empty&, Empty&) {} 
}

/* #2 The following function has no arguments. 
   There is no 'unknown argument list' as we do
   in C.
*/
void my_function() { 
  cout << "whoa! an error\n"; // #3 using can be scoped, as it is in main below
  // and this doesn't affect things outside of that scope
}

int main() {
  using namespace std; /* #4 you can use using in function scopes */
  cout << sizeof(Empty) << "\n"; /* #5 sizeof(Empty) is never 0 */
  /* #6 falling off of main without an explicit return means "return 0;" */
}

Нет, расширение std абсолютно нормально для нет, и стандарт явно запрещает это (за одним исключением: перегрузки swap).

Konrad Rudolph 21.02.2009 15:40

Спасибо. Я ошибся. Но это просто своп или стандарт говорит: только специализации стандартных шаблонов?

dirkgently 21.02.2009 15:54

Допускается специализация шаблонов в std, если специализация зависит от типа, определенного пользователем. Обмен не ограничен.

Johannes Schaub - litb 15.10.2009 15:15

Однако ваш конкретный пример недействителен, потому что ваша специализация не соответствует ни одной сигнатуре шаблона функции. У вас должно быть два эталонных параметра и т. д. :)

Johannes Schaub - litb 15.10.2009 15:16

«Для программы на C++ не определено добавлять объявления или определения в пространство имен std или пространства имен в пространстве имен std, если не указано иное». (17.4.3.1/1) Вы не можете перегрузить std :: swap и т. д., Но можете специализировать их: «Программа может добавлять специализации шаблона для любого стандартного шаблона библиотеки в пространство имен std. Такая специализация ... приводит к undefined, если только объявление не зависит от пользовательского имени внешней связи и если специализация не соответствует требованиям стандартной библиотеки для исходного шаблона ". (17.4.3.1/1)

Roger Pate 26.02.2010 10:48

Обратите внимание, что std :: swap, в частности, не может быть частично специализированным (это функция) и не может быть перегружен (см. Стандартную цитату выше), поэтому вы должны сделать что-то еще для шаблонов, которые вы пишете. Пример использования ADL с std :: swap в stackoverflow.com/questions/2197141/….

Roger Pate 26.02.2010 10:51

@Konrad: Где указано это исключение? Я не верю, что он существует.

Roger Pate 26.02.2010 10:56

@ Роджер, ты прав. Я имел в виду специализацию, а не перегрузку.

Konrad Rudolph 27.02.2010 18:36

Идиома косвенного преобразования:

Suppose you're designing a smart pointer class. In addition to overloading the operators * and ->, a smart pointer class usually defines a conversion operator to bool:

template <class T>
class Ptr
{
public:
 operator bool() const
 {
  return (rawptr ? true: false);
 }
//..more stuff
private:
 T * rawptr;
};

The conversion to bool enables clients to use smart pointers in expressions that require bool operands:

Ptr<int> ptr(new int);
if (ptr ) //calls operator bool()
 cout<<"int value is: "<<*ptr <<endl;
else
 cout<<"empty"<<endl;

Furthermore, the implicit conversion to bool is required in conditional declarations such as:

if (shared_ptr<X> px = dynamic_pointer_cast<X>(py))
{
 //we get here only of px isn't empty
} 

Alas, this automatic conversion opens the gate to unwelcome surprises:

Ptr <int> p1;
Ptr <double> p2;

//surprise #1
cout<<"p1 + p2 = "<< p1+p2 <<endl; 
//prints 0, 1, or 2, although there isn't an overloaded operator+()

Ptr <File> pf;
Ptr <Query> pq; // Query and File are unrelated 

//surprise #2
if (pf==pq) //compares bool values, not pointers! 

Решение: используйте идиому «косвенное преобразование», преобразовав указатель в член данных [pMember] в bool, чтобы было только одно неявное преобразование, которое предотвратит вышеупомянутое неожиданное поведение: pMember-> bool, а не bool-> something еще.

но также см. safe bool idiom

sehe 04.12.2011 06:01

Обратите внимание на разницу между инициализацией указателя на бесплатную функцию и указателя на функцию-член:

функция-член:

struct S
{
 void func(){};
};
int main(){
void (S::*pmf)()=&S::func;//  & is mandatory
}

и бесплатная функция:

void func(int){}
int main(){
void (*pf)(int)=func; // & is unnecessary it can be &func as well; 
}

Благодаря этому избыточному & вы можете добавлять манипуляторы потоков, которые являются бесплатными функциями, в цепочку без него:

cout<<hex<<56; //otherwise you would have to write cout<<&hex<<56, not neat.

Эмуляция переосмыслить актерский состав с помощью статический бросок:

int var;
string *str = reinterpret_cast<string*>(&var);

приведенный выше код эквивалентен следующему:

int var;    
string *str = static_cast<string*>(static_cast<void*>(&var));

Какой в ​​этом смысл?

Zifre 16.04.2009 03:50

Зифре, ничего. но результат в обоих случаях один и тот же. см. здесь: informit.com/blogs/…

Özgür 16.04.2009 04:14

Я действительно не вижу в этом "хитрости", поскольку ваш второй пример в основном просто описывает то, что происходит с reinterpret_cast. Вы также могли сделать это: string str = (строка) (void *) (& var); Кроме того, разве ваш static_cast <void> не должен быть static_cast <void *>?

Jared 22.06.2009 08:02

@Jared, никакого прикола. Я просто не знал, как работает reinterpret_cast, и хотел им поделиться. Вам даже не нужно добавлять (void *), ведь кастинг в стиле C включает как reinterpret_cast, так и static_cast. Вы правы насчет void *, спасибо.

Özgür 22.06.2009 11:36

Это неправда. Двойное статическое приведение - это то, что обычно делает считать reinterpret_cast. Но это не так. Они не эквивалентны. static_cast гарантирует, что он даст указатель на тот же адрес. reinterpret_cast просто предоставляет отображение, определяемое реализацией.

jalf 23.09.2009 17:00

static_cast гарантирует только тот же адрес, если вы вернетесь к тому же типу, который был у вас до преобразования в void*. В Стандарте есть некоторая скрытая тайна о некоторой особой обработке двойного static_cast, как показано в этом ответе, но мне еще нужно увидеть, чтобы кто-нибудь доказал реальное существование этой специальной обработки помимо формулировок «есть это, но я не знаю, где».

Johannes Schaub - litb 15.10.2009 15:11

Класс и структура class-keys почти идентичны. Основное отличие состоит в том, что классы по умолчанию имеют частный доступ для членов и баз, а структуры по умолчанию - общедоступные:

// this is completely valid C++:
class A;
struct A { virtual ~A() = 0; };
class B : public A { public: virtual ~B(); };

// means the exact same as:
struct A;
class A { public: virtual ~A() = 0; };
struct B : A { virtual ~B(); };

// you can't even tell the difference from other code whether 'struct'
// or 'class' was used for A and B

Объединения также могут иметь члены и методы и по умолчанию имеют общий доступ, как и структуры.

В части инициализации цикла for можно объявлять не только переменные, но также классы и функции.

for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) {
    ...
}

Это позволяет использовать несколько переменных разных типов.

Приятно осознавать, что вы можете это сделать, но лично я бы очень старался избегать подобных действий. В основном потому, что его трудно читать.

Zoomulator 11.07.2009 01:24

@Sir: Тем не менее, это может быть интересно использовать для сгенерированного кода или макрокоманды.

sbi 05.05.2010 18:07

-1: Извините, но, к сожалению, это не работает на VS2008. for (struct {int a; float b;} цикл = {1, 2} ;;); причины: "ошибка C2332: 'struct': отсутствует имя тега"

Valentin Heinitz 24.11.2010 20:33

На самом деле, в этом контексте будет работать пара: for (std :: pair <int, float> loop = std :: make_pair (1,2); loop.first> 0; loop.second + = 1)

Valentin Heinitz 24.11.2010 20:44

@Valentin, тогда я рекомендую вам попробовать сделать отчет об ошибке VS2008 вместо того, чтобы голосовать против скрытой функции. Очевидно, это вина вашего компилятора.

Johannes Schaub - litb 25.11.2010 00:21

Хм, в msvc10 тоже не работает. Как грустно :(

avakar 25.11.2010 01:25

@avakar на самом деле, gcc представил ошибку, из-за которой он тоже отклоняется, в v4.6 :) см. gcc.gnu.org/bugzilla/show_bug.cgi?id=46791

Johannes Schaub - litb 30.12.2010 01:35

Напоминает мне о «напишите программу на C, чтобы выводить свое имя, но не используйте точки с запятой в вашей программе». Мы просто используем если (printf ("Nav"))

Nav 05.02.2011 11:54

Добавление ограничения в шаблоны.

У примитивных типов есть конструкторы.

int i(3);

работает.

Это не конструктор, это просто форма инициализации, а именно прямая инициализация. Примитивные типы не имеют конструкторов.

GManNickG 07.01.2011 02:16

@Midas: он инициализировал i значением 3. Этот синтаксис обеспечивает единообразие с пользовательскими типами, что особенно полезно в шаблонах.

André Caron 21.05.2011 18:14

Мне кажется, что о безымянных пространствах имен мало кто знает:

namespace {
  // Classes, functions, and objects here.
}

Безымянные пространства имен ведут себя так, как если бы они были заменены:

namespace __unique_name__ { /* empty body */ }
using namespace __unique_name__;
namespace __unique_name__ {
  // original namespace body
}

«.. где все вхождения [этого уникального имени] в единице перевода заменены одним и тем же идентификатором, и этот идентификатор отличается от всех других идентификаторов во всей программе». [C++ 03, 7.3.1.1/1]

К сожалению, использование одного только пространства имен anon по-прежнему оставляет внешние (хотя и не имеющие ссылки) символы в вашей последней ссылке. Пока это не изменится, если вы заботитесь о конечном двоичном размере, лучше всего объявить любые локальные функции файла и статические переменные.

Jon 27.10.2009 21:09

@Jon: А какая разница после удаления исполняемых файлов?

Roger Pate 26.02.2010 11:24

Вы можете получить доступ к защищенным данным и функциям любого класса без неопределенного поведения и с ожидаемой семантикой. Читайте дальше, чтобы узнать, как это сделать. Прочтите также об этом отчет о дефектах.

Обычно C++ запрещает вам доступ к нестатическим защищенным членам объекта класса, даже если этот класс является вашим базовым классом.

struct A {
protected:
    int a;
};

struct B : A {
    // error: can't access protected member
    static int get(A &x) { return x.a; }
};

struct C : A { };

Это запрещено: вы и компилятор не знаете, на что на самом деле указывает ссылка. Это может быть объект C, и в этом случае класс B не имеет никакого отношения к своим данным. Такой доступ предоставляется только в том случае, если x является ссылкой на производный класс или производный от него. И он может позволить произвольному фрагменту кода читать любой защищенный член, просто создавая «одноразовый» класс, который считывает члены, например std::stack:

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        static std::deque<int> &get(std::stack<int> &s) {
            // error: stack<int>::c is protected
            return s.c;
        }
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = pillager::get(s);
}

Конечно, как видите, это нанесет слишком большой ущерб. Но теперь указатели на элементы позволяют обойти эту защиту! Ключевым моментом является то, что тип указателя члена привязан к классу, который фактически содержит упомянутый член - нет к классу, который вы указали при получении адреса. Это позволяет нам обойти проверку

struct A {
protected:
    int a;
};

struct B : A {
    // valid: *can* access protected member
    static int get(A &x) { return x.*(&B::a); }
};

struct C : A { };

И, конечно же, это также работает с примером std::stack.

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        static std::deque<int> &get(std::stack<int> &s) {
            return s.*(pillager::c);
        }
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = pillager::get(s);
}

Это будет еще проще с объявлением using в производном классе, которое делает имя члена общедоступным и ссылается на член базового класса.

void f(std::stack<int> &s) {
    // now, let's decide to mess with that stack!
    struct pillager : std::stack<int> {
        using std::stack<int>::c;
    };

    // haha, now let's inspect the stack's middle elements!
    std::deque<int> &d = s.*(&pillager::c);
}

Указатели на элементы и оператор указателя на член -> *

#include <stdio.h>
struct A { int d; int e() { return d; } };
int main() {
    A* a = new A();
    a->d = 8;
    printf("%d %d\n", a ->* &A::d, (a ->* &A::e)() );
    return 0;
}

Для методов (a -> * & A :: e) () немного похож на Function.call () из javascript

var f = A.e
f.call(a) 

Для участников это немного похоже на доступ с помощью оператора []

a['d']

Многие знают о метафункции identity / id, но есть хороший вариант использования для случаев, не связанных с шаблоном: Простота написания объявлений:

// void (*f)(); // same
id<void()>::type *f;

// void (*f(void(*p)()))(int); // same
id<void(int)>::type *f(id<void()>::type *p);

// int (*p)[2] = new int[10][2]; // same
id<int[2]>::type *p = new int[10][2];

// void (C::*p)(int) = 0; // same
id<void(int)>::type C::*p = 0;

Это очень помогает при расшифровке объявлений C++!

// boost::identity is pretty much the same
template<typename T> 
struct id { typedef T type; };

Интересно, но изначально у меня были проблемы с более при чтении некоторых из этих определений. Другой способ решить проблему с декларациями C++ - написать несколько псевдонимов типов шаблонов: template<typename Ret,typename... Args> using function = Ret (Args...); template<typename T> using pointer = *T; -> pointer<function<void,int>> f(pointer<function<void,void>>);, pointer<void(int)> f(pointer<void()>); или function<pointer<function<void,int>>,pointer<function<void,v‌​oid>>> f;.

bames53 09.01.2012 19:27

Я считаю, что создание рекурсивных шаблонов довольно круто:

template<class int>
class foo;

template
class foo<0> {
    int* get<0>() { return array; }
    int* array;  
};

template<class int>
class foo<i> : public foo<i-1> {
    int* get<i>() { return array + 1; }  
};

Я использовал это для создания класса с 10-15 функциями, которые возвращают указатели на различные части массива, поскольку используемый мной API требовал одного указателя на функцию для каждого значения.

Т.е. программирование компилятора для генерации набора функций с помощью рекурсии. Проще простого. :)

main () не требует возвращаемого значения:

int main(){}

это самая короткая действующая программа на C++.

Технически нет, потому что вам нужна новая строка, чтобы она была действительной: p

Kaz Dragon 20.11.2009 20:02

@Kaz: перевод строки есть, нажмите «изменить», чтобы просмотреть исходный код. :)

Roger Pate 26.02.2010 10:42

Вы можете использовать шаблоны битовых полей.

template <size_t X, size_t Y>
struct bitfield
{
    char left  : X;
    char right : Y;
};

Я еще не придумал для этого какой-либо цели, но это, черт возьми, меня удивило.

Смотрите здесь, где я недавно предложил это для n-битной арифметики: stackoverflow.com/questions/8309538/…

sehe 04.12.2011 05:08

Мне больше всего нравится (на данный момент) отсутствие сематики в таких утверждениях, как А = В = С. Какое значение A в основном не определено.

Подумайте об этом:

class clC
{
public:
   clC& operator=(const clC& other)
   {
      //do some assignment stuff
      return copy(other);
   }
   virtual clC& copy(const clC& other);
}

class clB : public clC
{
public:
  clB() : m_copy()
  {
  }

  clC& copy(const clC& other)
  {
    return m_copy;
  }

private:
  class clInnerB : public clC
  {
  }
  clInnerB m_copy;
}

теперь A может иметь тип, недоступный для каких-либо объектов, кроме объектов типа clB, и иметь значение, не связанное с C.

да - вы в основном говорите, что вы можете использовать перегрузку для изменения семантики? Теперь, если бы вы продемонстрировали, что семантика a=b=c не была исправлена ​​даже для встроенных типов / операторов ... Это было бы интересно. § 3.10Например, встроенные операторы присваивания ожидают, что левый операнд является lvalue, а правый операнд - prvalue, и в результате выдают lvalue. Определяемые пользователем операторы - это функции, а категории ожидаемых и получаемых ими значений определяются их параметрами и типами возвращаемых значений.

sehe 04.12.2011 06:10

@sehe, как насчет перегрузки для int, чтобы всегда назначать, скажем, 1/3 c для a в a = b = c?

Rune FS 04.12.2011 23:05

как вы определите нестатический членoperator= на учебный класс (sic) int?

sehe 05.12.2011 01:43

@ он никогда не говорил, что я буду

Rune FS 05.12.2011 14:04

how about an overload for int to always assign say 1/3 of c to a in a=b=c? для меня не материализуется в юридический кодекс. Не могли бы вы показать, что это у вас за иметь в виду? Вы заявляете lack of semantics как скрытая особенность языка C++. Думаю, имеет смысл объяснить, что вы имели в виду?

sehe 05.12.2011 14:13

@sehe, вы утверждаете, что семантика оператора присваивания является исправлена ​​в С ++? что вы можете обосновать (с уверенностью), что a будет назначено в связи с b и c в утверждении a = b = c?

Rune FS 06.12.2011 14:27

Ой. Тогда это неприятно. Я думал, вы каким-то образом предполагаете, что это «недооценка» и скрытая функция. Очевидно, это задумано. Без этого понятия не было бы такой вещи, как шаблоны выражений, поэтому не было бы Blitz ++, Eigen, Boost Proto (черт возьми, 25% ускорения) и т. д. И так далее, не могли бы вы тогда сказать то же самое о C#? ideone.com/6dxZG

sehe 06.12.2011 14:48

Семантика фиксируются для встроенных типов. Вы также можете рассуждать об этом для нестандартных типов, жестко и формально. (Если operator= принимает const&, значение не будет затираться.). Ваше вступительное предложение слишком широкое: the lack of sematics in a statement like A=B=C. Не просто любое утверждение: утверждение, включающее типы с нестандартной семантикой. Ну что? И это не недостаток семантики, а изобилие возможностей. (Первое предполагает языковой недостаток)

sehe 06.12.2011 14:57

то, что любой данный оператор может означать любую данную вещь, по определению является отсутствием семантики. Да, это может создать изобилие возможностей, но недостаток средств - это недостаток смысла

Rune FS 09.12.2011 13:42

функции void могут возвращать значения void

Малоизвестно, но следующий код подойдет

void f() { }
void g() { return f(); }

А также следующий странно выглядящий

void f() { return (void)"i'm discarded"; }

Зная об этом, вы можете воспользоваться преимуществами в некоторых сферах. Один пример: функции void не могут возвращать значение, но вы также можете не просто ничего не возвращать, потому что они могут быть созданы с непустым. Вместо сохранения значения в локальной переменной, что вызовет ошибку для void, просто верните значение напрямую.

template<typename T>
struct sample {
  // assume f<T> may return void
  T dosomething() { return f<T>(); }

  // better than T t = f<T>(); /* ... */ return t; !
};

Предотвращение вызова оператора запятой перегрузок оператора

Иногда вы правильно используете оператор запятой, но вы хотите, чтобы ни один пользовательский оператор запятой не мешал, потому что, например, вы полагаетесь на точки последовательности между левой и правой стороной или хотите убедиться, что ничто не мешает желаемому действие. Вот где в игру вступает void():

for(T i, j; can_continue(i, j); ++i, void(), ++j)
  do_code(i, j);

Не обращайте внимания на заполнители, которые я поставил для условия и кода. Что важно, так это void(), который заставляет компилятор использовать встроенный оператор запятой. Иногда это может быть полезно при реализации классов признаков.

Я просто использовал это, чтобы прикончить свой игнорировать выражение излишнего выражения. :)

GManNickG 04.02.2011 05:17

Вы можете просмотреть все предопределенные макросы с помощью переключателей командной строки с некоторыми компиляторами. Это работает с gcc и icc (компилятор Intel C++):

$ touch empty.cpp
$ g++ -E -dM empty.cpp | sort >gxx-macros.txt
$ icc -E -dM empty.cpp | sort >icx-macros.txt
$ touch empty.c
$ gcc -E -dM empty.c | sort >gcc-macros.txt
$ icc -E -dM empty.c | sort >icc-macros.txt

Для MSVC они перечислены в единственное место. Они могут быть задокументированы в одном месте и для других, но с помощью приведенных выше команд вы можете четко определить видеть, что определено, а что нет, и какие именно значения используются, после применения всех других параметров командной строки.

Сравните (после сортировки):

 $ diff gxx-macros.txt icx-macros.txt
 $ diff gxx-macros.txt gcc-macros.txt
 $ diff icx-macros.txt icc-macros.txt

Тернарный условный оператор ?: требует, чтобы его второй и третий операнды имели "приемлемые" типы (говоря неформально). Но это требование имеет одно исключение (каламбур): либо второй, либо третий операнд может быть выражением throw (имеющим тип void), независимо от типа другого операнда.

Другими словами, с помощью оператора ?: можно написать следующие правильно допустимые выражения C++.

i = a > b ? a : throw something();

Кстати, тот факт, что выражение throw на самом деле является выражение (типа void), а не оператором, является еще одной малоизвестной особенностью языка C++. Это, помимо прочего, означает, что следующий код абсолютно верен.

void foo()
{
  return throw something();
}

хотя в этом нет особого смысла (возможно, в каком-то универсальном коде шаблона это может пригодиться).

Как бы то ни было, у Нила есть вопрос по этому поводу: stackoverflow.com/questions/1212978/…, просто для дополнительной информации.

GManNickG 26.02.2010 10:09
  1. map::insert(std::pair(key, value)); не перезаписывает, если значение ключа уже существует.

  2. Вы можете создать экземпляр класса сразу после его определения: (Я мог бы добавить, что эта функция дала мне сотни ошибок компиляции из-за отсутствия точки с запятой, и я никогда не видел, чтобы кто-то использовал ее в классах)

    class MyClass {public: /* code */} myClass;
    

Я знаю человека, который одновременно определяет геттер и сеттер только одним методом. Так:

class foo
{
    int x;

    int* GetX(){
        return &x;
    }
}

Теперь вы можете использовать это как получатель, как обычно (ну, почти):

int a = *GetX();

и как установщик:

*GetX() = 17;

Хотя я бы сменил имя. Это приводит к большой путанице.

Tobias Langner 26.02.2010 12:04

Он использует его только тогда, когда программирует что-то для себя.

aheld 26.02.2010 12:10

Зачем ему в таком случае возвращать int* вместо int& !?

Johann Gerell 26.02.2010 12:33

Без обид, но мой голос против вашего друга. :) Нет причин возвращать указатель, если подойдет неконстантная ссылка. Кроме того, нет смысла даже иметь геттер, это просто пустая трата места; сделайте переменную-член общедоступной.

GManNickG 26.02.2010 22:40

На самом деле это не пустая трата места, поскольку он объявлен встроенным;)

Joe D 05.05.2010 18:37

Это лишает смысла использование геттеров и сеттеров, открывая внутреннюю реализацию, тем самым делая невозможным изменение реализации позже, не затрагивая каждого вызывающего. Вашему другу удалось совместить все недостатки прямого доступа к участникам со всеми недостатками геттеров / сеттеров. Хорошая работа.

Ferruccio 19.11.2010 02:57

@Ferruccio: и вы забыли упомянуть о проблемах времени жизни из-за возвращенных ссылок / указателей на участников .. Действительно ужасно плохой код.

sehe 04.12.2011 06:17

Метапрограммирование шаблона есть.

На самом деле не скрытая функция, а чистая классность:

#define private public 

Это даже не функция, это просто незаконно переопределять ключевые слова.

Georg Fritzsche 05.07.2010 04:46

Говорит где? Этап предварительной обработки применяется еще до того, как ключевые слова будут учтены анализатором / лексером. Вы можете переопределить практически ВСЕ.

mihai 13.11.2011 00:25

Чтобы доказать это, попробуйте следующее: #define while if void main() { while(1); }

mihai 13.11.2011 00:26

@mihai: Здесь так сказано: § 17.6.1.3 ad 7: Identifiers that are keywords or operators in C++ shall not be defined as macros in C++ standard library headers., в сочетании с Одно правило определения. Таким образом, единственный выход - включить нет любые заголовки стандартной библиотеки и не использовать какой-либо код стандартной библиотеки.

sehe 04.12.2011 06:21

Вы можете вернуть ссылку на переменную как часть функции. У него есть несколько применений, в основном для создания ужасного кода:

int s ;
vector <int> a ;
vector <int> b ;

int &G(int h)
{
    if ( h < a.size() ) return a[h] ;
    if ( h - a.size() < b.size() ) return b[ h - a.size() ] ;
    return s ;
}

int main()
{
    a = vector <int> (100) ;
    b = vector <int> (100) ;

    G( 20) = 40 ; //a[20] becomes 40
    G(120) = 40 ; //b[20] becomes 40
    G(424) = 40 ; //s becomes 40
}

Я знаю, что не должен спрашивать об этом, но почему голос против?

Martín Fixman 24.06.2010 18:03

Я бы не назвал это скрытым - распространенными примерами являются операторы потока, operator* для итераторов, префикс ++, std::vector<T>::front(), составные присваивания, ...

Georg Fritzsche 05.07.2010 04:43

Классные местные классы:

struct MyAwesomeAbstractClass
{ ... };


template <typename T>
MyAwesomeAbstractClass*
create_awesome(T param)
{
    struct ans : MyAwesomeAbstractClass
    {
        // Make the implementation depend on T
    };

    return new ans(...);
}

довольно аккуратно, поскольку он не загрязняет пространство имен бесполезными определениями классов ...

Одна скрытая функция, даже скрытая для Разработчики GCC, - это инициализация элемента массива с помощью строкового литерала. Предположим, у вас есть структура, которая должна работать с массивом C, и вы хотите инициализировать член массива содержимым по умолчанию.

struct Person {
  char name[255];
  Person():name("???") { }
};

Это работает и работает только с массивами символов и инициализаторами строковых литералов. strcpy не требуется!

Может быть, это только я, но это кажется очевидным, учитывая, что void foo(){char p[255] = "daddy";} также является законным способом инициализации массива.

P Daddy 09.07.2010 01:11

@PDaddy, это не слишком очевидно, потому что, хотя вещь = "foo" часто выполняется в коде, она почти никогда не выполняется для инициализаторов членов. Я никогда не видел этого раньше, пока не прочитал Стандарт и не обнаружил, что это разрешено и работает с другими компиляторами.

Johannes Schaub - litb 09.07.2010 01:17

Не пытаясь быть аргументированным, но как новичок, это было следующее, что пришло в голову после попытки чего-то вроде class C{char p[255] = "daddy";}; и получения «только статические константные интегральные элементы данных могут быть инициализированы классом». Но в то время я только недавно узнал о списках инициализации, так что, возможно, это облегчило задачу. В любом случае, это, безусловно, наглядный пример того, чем списки инициализации отличаются от присваивания в теле конструктора.

P Daddy 09.07.2010 04:55

Еще одна скрытая особенность заключается в том, что вы можете вызывать объекты класса, которые можно преобразовать в указатели на функции или ссылки. Разрешение перегрузки выполняется по их результатам, и аргументы передаются безупречно.

template<typename Func1, typename Func2>
class callable {
  Func1 *m_f1;
  Func2 *m_f2;

public:
  callable(Func1 *f1, Func2 *f2):m_f1(f1), m_f2(f2) { }
  operator Func1*() { return m_f1; }
  operator Func2*() { return m_f2; }
};

void foo(int i) { std::cout << "foo: " << i << std::endl; }
void bar(long il) { std::cout << "bar: " << il << std::endl; }

int main() {
  callable<void(int), void(long)> c(foo, bar);
  c(42); // calls foo
  c(42L); // calls bar
}

Они называются «суррогатными функциями вызова».

Когда вы говорите, что по их результату выполняется разрешение перегрузки, вы имеете в виду, что он фактически преобразует его в оба функтора, а затем выполняет разрешение перегрузки? Я пробовал напечатать что-то в операторе Func1 * () и в операторе Func2 * (), но, похоже, он выбрал правильный, когда выяснил, какой оператор преобразования вызвать.

navigator 08.07.2010 15:32

@navigator, да, он концептуально преобразуется в оба, а затем выбирает лучшее. На самом деле вызывать их не нужно, потому что он знает по типу результата, что они уже дадут. Фактический вызов совершается, когда выясняется, что было окончательно выбрано.

Johannes Schaub - litb 08.07.2010 22:51

Еще одна скрытая функция, которая не работает в C, - это функциональность унарного оператора +. Вы можете использовать его для продвижения и разложения всяких вещей

Преобразование перечисления в целое число

+AnEnumeratorValue

И значение вашего перечислителя, которое раньше имело тип перечисления, теперь имеет идеальный целочисленный тип, который может соответствовать его значению. Вручную вы вряд ли узнаете этот тип! Это необходимо, например, когда вы хотите реализовать перегруженный оператор для перечисления.

Получить значение из переменной

Вы должны использовать класс, который использует статический инициализатор внутри класса без определения вне класса, но иногда он не может связать? Оператор может помочь создать временный объект, не делая предположений или зависимостей от его типа.

struct Foo {
  static int const value = 42;
};

// This does something interesting...
template<typename T>
void f(T const&);

int main() {
  // fails to link - tries to get the address of "Foo::value"!
  f(Foo::value);

  // works - pass a temporary value
  f(+Foo::value);
}

Распад массива на указатель

Вы хотите передать функции два указателя, но это просто не сработает? Оператор может помочь

// This does something interesting...
template<typename T>
void f(T const& a, T const& b);

int main() {
  int a[2];
  int b[3];
  f(a, b); // won't work! different values for "T"!
  f(+a, +b); // works! T is "int*" both time
}

Правило доминирования полезно, но малоизвестно. В нем говорится, что даже если в неуникальном пути через решетку базового класса, поиск по имени частично скрытого члена уникален, если член принадлежит виртуальному базовому классу:

struct A { void f() { } };

struct B : virtual A { void f() { cout << "B!"; } };
struct C : virtual A { };

// name-lookup sees B::f and A::f, but B::f dominates over A::f !
struct D : B, C { void g() { f(); } };

Я использовал это для реализовать выравнивание-поддержку, который автоматически определяет строжайшее выравнивание с помощью правила доминирования.

Это относится не только к виртуальным функциям, но и к именам typedef, статическим / невиртуальным членам и всему остальному. Я видел, как он использовался для реализации перезаписываемых свойств в метапрограммах.

Аккуратный. Какая-то конкретная причина, по которой вы включили struct C в свой пример ...? Ваше здоровье.

Tony Delroy 11.05.2011 08:29

Другие вопросы по теме