void bar(std::string&& x) {}
void foo(std::string&& x) {
bar(std::move(x));
}
int main() {
std::string x = "Test";
foo (std::move(x));
return 0;
}
template <class T>
void foo(T&& x) {
bar(std::forward<T>(x));
}
enum class Shape { NONE, LINE, ELLIPSE, RECTANGLE };
... в форме...
Shape shareToDraw = Shape::NONE;
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&)
Ну и ещё парочка структур для раскрутки стека и подстановки адресов…