class a final
{
private:
class ex
{
int f = 0;
};
static void foo() { std::cout << 'f' << std::endl; };
public:
class F
{
public:
F() { foo(); };
private:
ex m_exes[4];
};
};
state ? (cout << b / a << endl, exit(0)) : state = (a == 0) && (b == 0);
exit(0)
или конструкцию присвоения внутри тернарного оператора.:
накладываются некоторые ограничения. Если выражения не проходят по ограничениям тернарного оператора, выражение тернарного оператора считается плохо сформированным, что и приводит к ошибке синтаксиса. void display()
{
glClear( GL_COLOR_BUFFER_BIT );
glBegin( GL_LINE_LOOP );
for( size_t index = 0; index < vertices.size(); ++index )
{
const Vertex& vertex = vertices[ ( index * 3 ) % vertices.size() ];
glVertex2i( vertex.x, vertex.y );
}
glEnd();
glFlush();
}
std::initializer_list
участвует в двух из трех.Foo bar = {...};
Foo bar{...};
std::initializer_list
выполняются только на этапе трансляции. В обоих случаях сперва транслятор попробует придумать std::initializer_list
. Если у аргументов типы разные (а приведение типов при такой записи не делается), то попробовать создать std::initializer_list
у транслятора не получится. Но если получилось, то транслятор уже итак знает число аргументов, переданных в конструктор.std::initializer_list
, с которым конструктор и вызывается.std::initializer_list
нельзя копировать, перемещать, сохранять в состоянии конструируемого объекта. std::initializer_list
не владеет отображаемой памятью, он только дает к ней доступ. s = loc::get("key"); // ambiguous overload for op=
std::wstring
(конструктор копирования и конструктор преобразования из const wchar_t*
) являются неявными.TaggedCWstr
так же являются неявными.std::wstring_view
.std::wstring
и этим позволять бесконтрольно обращаться к динамической памяти без явного понимания этого процесса. Лучше для получения std::wstring
сделать operator *
, а еще лучше - вообще не ломать семантику операторов и сделать метод с говорящим именем. std::auto_ptr
является устаревшим и удален из стандартной библиотеки начиная со стандарта C++17.std::unique_ptr
.std::auto_ptr
мог следить только за памятью одного экземпляра. Передать туда память массива можно, но деструктор будет вызван только у первого экземпляра массива. В общем смысле это означает утечку памяти.std::unique_ptr
, наоборот, способен контролировать память как единичного экземпляра, так и массива экземпляров. Еще в отличии от своего устаревшего товарища, std::unique_ptr
способен спокойно передавать свое состояние, не создавая возможность двойного освобождения памяти. В дополнение, std::unique_ptr
еще способен пользоваться нестандартными деструкторами, что очень кстати при работе, например, с COM-объектами или нестандартной схемой аллокации памяти.main
.void swap(T& first, T& second)
.std::swap
.template <typename T1>
void out_array(const T1* array, size_t arraySize)
arraySize
тоже не нужен. Ты работаешь с сырыми массивами фиксированной длины и можешь дать своему шаблону функции понимание длины такого массива. Делается это так.template < typename TElementType, size_t LENGTH >
void out_array( const TElementType array[ LENGTH ] )
char array2[n] = { '2','1','4','5','3','-3','-1','-2','-4','-5' };
'2'
не будет эквивалентно 2
. С этого момента сортировка будет сортировать по коду символьной таблицы, а не по значению числа. Это ошибка, т.к. это явно не то поведение, которое ты ждешь.'-3'
и.т.д. Тип у такой конструкции будет int
, а не char
. Компилятор тебе именно об этом и пытается сказать.int array3[n] = atoi(array2);
int array3[n];
std::transform(
std::begin( array2 ), std::end( array2 ), std::begin( array3 ),
[]( const char item ) -> int { return item; }
);
std::transform
. Node root = {T{}, nullptr, nullptr};
Node root{T{}, nullptr, nullptr};
template <typename T>
struct NameOf {};
NameOf
. Определение это я сразу назову неправильным, потому что от этого шаблона можно инстанцировать тип и получить совершенно непонятную ошибку компиляции дальше. Должно быть так, чтобы тип из общей формы шаблона инстанцировать было нельзя.#define DEF_TYPENAME(type) template <> \
struct NameOf<type> {\
static const char value[];\
};\
const char NameOf<type>::value[] = #type;
NameOf
для переданного типа. Тут я скажу только то, что можно сделать полностью иначе и макрос тут полностью не нужен.DEF_TYPENAME(int)
DEF_TYPENAME(double)
DEF_TYPENAME(long double)
DEF_TYPENAME(float)
DEF_TYPENAME(char)
DEF_TYPENAME(long)
DEF_TYPENAME(unsigned)
DEF_TYPENAME(unsigned long)
;
все таки был бы более уместен в этом месте, т.к. сейчас код выглядит несвязной простыней без структуры. Сразу хочется сказать что тут синтаксическая ошибка, хоть на самом деле это и не так.NameOf
.template <typename T, typename ...types>
void printTypes(T)
{
std::cout << NameOf<T>::value << std::endl;
}
template <typename T, typename ...types>
void printTypes(T, types... t)
{
std::cout << NameOf<T>::value << ", ";
printTypes(t...);
}
printTypes
из шаблона. Я сейчас остановлюсь лишь на второй ветви вывода.template <typename T, typename ...types>
void printTypes(T, types... t)
{
std::cout << NameOf<T>::value << ", ";
printTypes(t...);
}
typename ...types
в объявлении шаблона говорит что типов ожидается от нуля и пока фантазия не кончится. Как пользоваться таким шаблонами - хорошо описано в документации по моей ссылке.std::cout << NameOf<T>::value << std::endl;
. ::new
является точкой обращения к аллокатору памяти процесса. К какому именно аллокатору идет обращение - как правило неизвестно. Это может быть dlmalloc
, mimalloc
или jemalloc
. Их много и перечислять можно долго. Это может быть и самостоятельно созданный по мотивам доклада Александреску аллокатор.::delete
). IMagicSpell::ShowDebugInfo
у тебя обращается к приватному полю IMagicSpell::SpellInfo
. Это поле недоступно для наследников, т.к. является приватным.Fireball::Fireball
, у тебя работает с приватным полем Fireball::SpellInfo
.IMagicSpell::SpellInfo
и Fireball::SpellInfo
являются разными, расположены в разных областях памяти объекта Fireball
и в принципе никак не могут пересечься своими данными.Fireball
будет с два MagicSpellInfo
плюс размер таблицы виртуальных символов.MagicSpellInfo
в иерархии - лишний. Fireball::SpellInfo
является дублирующим, лишним полем, которое не позволяет добиться необходимой тебе функциональности. Ведь в обоих твоих классах тебе надо работать с одними данными, с полем IMagicSpell::SpellInfo
.Fireball::SpellInfo
.class Fireball: public IMagicSpell {
public:
Fireball() {
SpellInfo.Name = "Огненный шар";
SpellInfo.Description = "Мощный огненный шар, сметающий все на своем пути.";
SpellInfo.Type = fire;
SpellInfo.MinDamage = 30;
SpellInfo.MaxDamage = 50;
SpellInfo.Range = 40;
SpellInfo.Distance = 120;
}
virtual ~Fireball() {}
virtual void Use() {
cout << "Boom!" << endl;
}
};
IMagicSpell::SpellInfo
является приватным и недоступен для потомков. Исправить это тоже очень легко. private
- приватный доступ, только для экземпляра текущего класса;protected
- ограниченный доступ, только для экземпляров текущего класса и всех публичных наследников; доступ ограничивается при ограниченном или приватном наследовании;public
- публичный доступ, для всех пользователей экземпляров класса.IMagicSpell::SpellInfo
с приватного на ограниченный. В этом случае экземпляры Fireball
смогут обращаться к IMagicSpell::SpellInfo
как к своему ограниченному полю.class IMagicSpell {
protected:
MagicSpellInfo SpellInfo;
public:
virtual ~IMagicSpell() {}
virtual void Use() {}
MagicSpellInfo GetInfo() {
return this->SpellInfo;
}
void ShowDebugInfo() {
cout << "Название: " << SpellInfo.Name << endl;
cout << "Описание: " << SpellInfo.Description << endl;
cout << "Тип: " << SpellInfo.Type << endl;
cout << "Мин. урон: " << SpellInfo.MinDamage << endl;
cout << "Макс. урон: " << SpellInfo.MaxDamage << endl;
cout << "Расстояние: " << SpellInfo.Distance << endl;
cout << "Радиус: " << SpellInfo.Range << endl;
}
};
get_substr("один", "два три четыре")
, ты в функцию передаешь два строковых литерала, тип которых - const char[ N ]
, где N
- это длина строки литерала включая терминальный символ '\0'
.const char*
в char*
недопустимо, поэтому компилятор и пишет тебе ошибку.char*
вообще не нужен, т.к. полностью все операции у тебя не приводят к изменению состояния строки. Заменить char*
на const char*
будет и логичнее, и понимаемость кода тоже улучшит. debug
и принимает аргумент, но вне отладочной конфигурации он этим аргументом не оперирует. В любом месте обращения к твоему макросу произойдет подстановка NULL
вместо всего обращения.NULL
в качестве подстановки не нужен. Зачем тебе в коде программы обилие висящих NULL
? Если описать макрос так:#ifdef DEBUG
#define debug(n) Serial.println("***"+String(n)+"***")
#else
#define debug(n)
#endif
operator<<(ofstream&, const Book&)
должен быть определен в пространстве имен std
, т.к. ofstream
определен именно в этом пространстве.namespace std
{
// use the `Book` type definitely from global namespace.
ofstream& operator<<(ofstream &of, const ::Book &book)
{
// ...
}
}
И еще вопрос: что нужно возвращать из этой функции?
glutPostRedisplay
.double GetCurrentTime()
{
using Duration = std::chrono::duration<double>;
return std::chrono::duration_cast<Duration>(
std::chrono::high_resolution_clock::now().time_since_epoch()
).count();
}
const double frame_delay = 1.0 / 60.0; // 60 FPS
double last_render = 0;
void OnIdle()
{
const double current_time = GetCurrentTime();
if( ( current_time - last_render ) > frame_delay )
{
last_render = current_time;
glutPostRedisplay();
}
}
double
помогает легче находить дельту между кадрами и контролировать ошибку дельты.5
будет являться числовым литералом с типом int
.5u
, то тип бы уже был unsigned int
. А если бы ты написал 5lu
, то тип бы уже был unsigned long int
.