template <class Device>
SomeFunction getProcAddress(const Device& device, const std::string& name);
template <>
inline SomeFunction getProcAddress<VkInstance>(
const VkInstance& device, const std::string& name)
{ return vkGetInstanceProcAddr(device, name.c_str()); }
template <class Device>
inline SomeFunction getProcAddress(const Device& device, const std::string& name)
{
if constexpr (std::is_same_v<Device, VkInstance>) {
return vkGetInstanceProcAddr(device, name.c_str());
} else {
// false нельзя! — проверка произойдёт в любом случае, будет компилироваться ветка или нет.
static_assert(std::always_false_v<Device>, "Unknown type");
}
}
wchar_t a;
if (a == L'Д') {}
std::string s; if (s.starts_with("Д")) {}
(Си++20!!) Но инструментарий поуже будет, а под Windows с разнобоем кодировок — не советую. template <size_t N>
void myA(const int(&arra)[N], const int(&arrb)[N])
{
auto arra_begin = std::begin(arra);
std::cout << *arra_begin << std::endl; // 1
}
// Си++20!!!!!!!!!!
void myB(std::span<int> arra, std::span<int> arrb)
{
auto arra_begin = std::begin(arra);
std::cout << *arra_begin << std::endl; // 1
}
int&& d = 5
) используется крайне редко, чтобы продлить время жизни временному объекту.string&& Container::give()
), и означает: это ссылка на объект, который спокойно можно выпотрошить. Или в типах параметров — Container& Container::operator = (string&& x)
: то есть принимает объект, который больше не нужен.std::vector<int> doSmth() {
std::vector<int> r;
r.push_back(42);
return r;
}
...
std::vector<int> a = doSmth();
std::vector<int> r;
— создадим в стековом фрейме переменную r.return r;
— vector(const vector&) в какое-то место, предложенное вызывающей функцией.std::vector<int> a = doSmth();
— vector(const vector&) из этого места в a.return vector<int>{ 42 };
— то ни одного), но у нас-то Си++03. Не много конструкторов? А ведь зная, что объект больше не нужен, можем просто перенести указатель из одного вектора в другой, точка — даже впрямую, без оптимизаций, никаких перевыделений памяти. Вот этот прямой перенос указателей я и назвал словом «выпотрошить объект».container = string("alpha") + "bravo";
Тут в контейнер перемещается временная строка.string a = "alpha"; container = a;
Не компилируется: строка a не временная.container1 = container2.give();
Перенос строки из контейнера в контейнер без выделения памяти.string a = "alpha"; container = std::move(a);
Компилируется (мы функцией move сказали: с обычным объектом обращаться как с временным), но в результате в строке a может быть что угодно и не стоит закладываться на дальнейшее её значение. Но объект после потрошения в любом случае должен быть корректным, ведь ему ещё вызывать деструктор :)myString=std::move(x)
или std::swap(myString, x)
. А они, в свою очередь — внутренняя string::op= и дружественная swap — прямыми играми с указателями. template <class T> struct string_traits;
template <class C, class T, class A>
struct string_traits<std::basic_string<C,T,A>> {
using Ch = C;
using Tr = T;
using Sv = std::basic_string_view<C,T>;
};
template <class C, class T>
struct string_traits<std::basic_string_view<C,T>> {
using Ch = C;
using Tr = T;
using Sv = std::basic_string_view<C,T>;
};
template <class C>
struct string_traits<const C*> {
using Ch = C;
using Tr = std::char_traits<C>;
using Sv = std::basic_string_view<C>;
};
template <class C, size_t N>
struct string_traits<C[N]> {
using Ch = C;
using Sv = std::basic_string_view<C>;
};
template<class S>
using s_v_t = typename string_traits<std::remove_cvref_t<S>>::Sv;
namespace detail {
template <class Sv>
Sv remainderSv(Sv s, Sv prefix)
{
if (s.length() <= prefix.length()
|| !s.starts_with(prefix))
return {};
return s.substr(prefix.length(), s.length() - prefix.length());
}
}
/// @brief remainderSv
/// Same for prefix only
template <class A>
inline auto remainderSv(const A& s, s_v_t<A> prefix) -> s_v_t<A>
{ return detail::remainderSv<s_v_t<A>>(s, prefix); }
Implicitly-declared move constructor
If no user-defined move constructors are provided for a class type (struct, class, or union), and all of the following is true:
- there are no user-declared copy constructors;
- there are no user-declared copy assignment operators;
- there are no user-declared move assignment operators;
- there is no user-declared destructor.
then the compiler will declare a move constructor as a non-explicit inline public member of its class with the signature T::T(T&&).
#include <iostream>
#include <memory>
#define IMPLEMENT_CLONE_1(Class, Keyword) \
virtual Class* vclone() const Keyword { return new Class(*this); } \
auto clone() const { return std::unique_ptr<Class>{vclone()}; }
#define IMPLEMENT_CLONE(Class) IMPLEMENT_CLONE_1(Class, override)
class BaseClass {
public:
IMPLEMENT_CLONE_1(BaseClass, )
virtual ~BaseClass() = default;
};
class MyClass : public BaseClass {
public:
IMPLEMENT_CLONE(MyClass)
void print() const { std::cout << "I'm a MyClass!" << std::endl; }
};
int main()
{
auto a = std::make_unique<MyClass>();
auto b = a->clone();
b->print();
return 0;
}
#include <iostream>
#include <memory>
class BaseClass {
public:
auto clone() const { return std::unique_ptr<BaseClass>{ vclone() }; }
protected:
virtual BaseClass* vclone() const { return new BaseClass(*this); }
};
template <class Super, class This>
class CloneImpl : public Super
{
public:
auto clone() const { return std::unique_ptr<This>{ static_cast<This*>(vclone()) }; }
protected:
CloneImpl* vclone() const override { return new This(*static_cast<const This*>(this)); }
};
class MyClass : public CloneImpl<BaseClass, MyClass> {
public:
void print() const { std::cout << "I'm a MyClass!" << std::endl; }
};
int main()
{
auto a = std::make_unique<MyClass>();
auto b = a->clone();
b->print();
return 0;
}
// MAIN.CPP
#include <iostream>
#include "tmpl.h"
void doFile1();
int main()
{
const char* x = "main";
outThing(x);
doFile1();
return 0;
}
// FILE1.CPP
#include "tmpl.h"
void doFile1()
{
const char* x = "doFile1";
outThing(x);
}
// TMPL.H
#pragma once
#include <iostream>
template <class T>
void outThing(const T& x)
{
std::cout << "The thing is " << x << std::endl;
}
Discarded input sections
.text$_Z8outThingIPKcEvRKT_
0x0000000000000000 0x50 debug/main.o
Linker script and memory map
.text$_Z8outThingIPKcEvRKT_
0x0000000140002900 0x50 debug/file1.o
0x0000000140002900 void outThing<char const*>(char const* const&)
Ну и ещё парочка структур для раскрутки стека и подстановки адресов…
double s = 0;
for (i...) {
s += Sum(m, &a[i][0]);
}
Если переменных в памяти потребуется слишком большое количество, которое не сможет вместить в себя сама аппаратная часть, произойдет перегрузка системы или её зависание.
Можете себе представить, если бы небезызвестная Battlefield 3 использовала такой метод работы с данными? В таком случае, самым заядлым геймерам пришлось бы перезагружать свои высоконагруженные системы кнопкой reset после нескольких секунд работы игры.
Если не уничтожать неиспользуемые объекты, очень скоро они заполнят весь объем ресурсов ПК.
#include <iostream>
union DoubleInt {
double asDouble;
uint64_t asInt;
};
static_assert(sizeof(double) == sizeof(uint64_t), "Strange machine with double != int64");
constexpr int BITS_MANTISSA = 52;
constexpr int BITS_EXPONENT = 11;
constexpr int BITS_SIGN = 1;
static_assert(BITS_MANTISSA + BITS_EXPONENT + BITS_SIGN == 64, "Programmer's funkup");
constexpr uint64_t MANTISSA_UNIT = uint64_t(1) << BITS_MANTISSA;
constexpr uint64_t MANTISSA_MASK = MANTISSA_UNIT - 1;
constexpr int EXPONENT_SHIFT = BITS_MANTISSA;
constexpr uint64_t EXPONENT_MAX = (uint64_t(1) << BITS_EXPONENT) - 1;
constexpr uint64_t EXPONENT_ORIGIN = EXPONENT_MAX >> 1;
constexpr uint64_t EXPONENT_MASK = EXPONENT_MAX << EXPONENT_SHIFT;
constexpr uint64_t EXPONENT_SHIFTED_ORIGIN = EXPONENT_ORIGIN << EXPONENT_SHIFT;
constexpr int SIGN_SHIFT = BITS_MANTISSA + BITS_EXPONENT;
constexpr uint64_t SIGN_MASK = uint64_t(1) << SIGN_SHIFT;
int main()
{
DoubleInt x { -3.45 };
// Простите уж, без денормализованных чисел
// Оставим знак и мантиссу
DoubleInt xMantissa = x;
xMantissa.asInt &= (MANTISSA_MASK | SIGN_MASK);
// И добавим туда стандартный нулевой порядок
xMantissa.asInt |= EXPONENT_SHIFTED_ORIGIN;
// Извлечём порядок
int exponent = ((x.asInt & EXPONENT_MASK) >> EXPONENT_SHIFT) - EXPONENT_ORIGIN;
std::cout << xMantissa.asDouble << "*2^" << exponent << std::endl;
return 0;
}