Bob*_*bby 1 c++ constructor destructor class micro-optimization
我的班级有两名成员。
class C {
typeA a;
typeB b;
};
Run Code Online (Sandbox Code Playgroud)
假设没有继承。对于这门课,我担心三件事。
a
和 的构造函数的b
调用顺序a
和 的析构函数的b
调用顺序a
和b
在内存中的布局我可能关心(1)和(2)的原因之一是线程同步。我可能关心 (3) 的一些原因是:
填充
具有两个以上类成员的空间局部性
第一个类成员的内存偏移量为 0
我希望能够同时控制(1)-(3)。然而,似乎当我选择内存中的顺序时,我也选择了构造函数和析构函数的顺序。有没有办法同时控制(1)-(3)?
是的。像这样拼写每个单独的成员:
union {typeA a;};
Run Code Online (Sandbox Code Playgroud)
这可以防止它们被自动构造和破坏,从而允许您手动执行(::new((void *)&a) typeA();
构造、a.~typeA();
破坏)。
必须提醒的是,如果您启用了异常,而C
的构造函数未启用noexcept
,并且其中一个构造函数抛出异常,则需要销毁已构造的成员。
以此为挑战,编写了一个具有此功能的元组类:
OrderedTuple<
// construction order
// key | type
// | | |
OrderedTupleElem<"a"_const, 1, int>,
OrderedTupleElem<"b"_const, 2, std::string>,
OrderedTupleElem<"c"_const, 0, float>
> foo;
foo = {42, "bar", 1.23f};
std::cout << foo.get<"b"_const>() << '\n'; // "bar"
Run Code Online (Sandbox Code Playgroud)
实施如下。
虽然到目前为止它似乎运行良好,即使存在异常,但如果您打算在生产中使用它,您的工作就是通过测试来覆盖它,以确保我不会弄乱 RAII。
这需要 C++23,但向后移植到 C++20 应该不难。(或者如果您去掉了字符串键,则转为 C++17。)
#include <algorithm>
#include <array>
#include <cstddef>
#include <tuple>
#include <type_traits>
#include <utility>
template <auto>
struct ValueTag {};
template <
auto K, // Arbitrary key.
std::size_t I, // Construction order index.
typename T // Element type.
>
struct OrderedTupleElem
{
static constexpr std::size_t index = I;
using type = T;
union {T value;};
constexpr OrderedTupleElem() {}
constexpr ~OrderedTupleElem() {}
constexpr T &DetailGetElem(ValueTag<K>) {return value;}
};
namespace detail
{
template <typename T>
constexpr bool IsOrderedTupleElemVar = false;
template <auto K, std::size_t I, typename T>
constexpr bool IsOrderedTupleElemVar<OrderedTupleElem<K, I, T>> = true;
template <typename T>
concept IsOrderedTupleElem = IsOrderedTupleElemVar<T>;
template <typename T, typename...>
struct FirstType {using type = T;};
}
template <detail::IsOrderedTupleElem ...P>
class OrderedTuple : P...
{
using P::DetailGetElem...;
static consteval auto MakeFuncArray(auto &&generate)
{
static_assert(sizeof...(P) > 0);
std::array<decltype(+generate.template operator()<typename detail::FirstType<P...>::type, 0>()), sizeof...(P)> ret{};
[&]<std::size_t ...I>(std::index_sequence<I...>)
{
([&]{
if (ret[P::index])
throw "Duplicate element index in `OrderedTuple<...>` template arguments.";
ret[P::index] = +generate.template operator()<P, I>();
}(), ...);
}(std::make_index_sequence<sizeof...(P)>{});
return ret;
}
constexpr void DestroyFromIndex(std::size_t i)
{
if constexpr (sizeof...(P) > 0)
{
static constexpr auto arr = MakeFuncArray([]<typename T, std::size_t>
{
return [](OrderedTuple &self) noexcept
{
using MemberType = T::type;
static_cast<T &>(self).value.~MemberType();
};
});
while (i > 0)
{
i--;
arr[i](*this);
}
}
}
struct ConstructorGuard
{
OrderedTuple &self;
std::size_t i = 0;
constexpr ConstructorGuard(OrderedTuple &self) : self(self) {}
ConstructorGuard(const ConstructorGuard &) = delete;
ConstructorGuard &operator=(const ConstructorGuard &) = delete;
constexpr ~ConstructorGuard()
{
self.DestroyFromIndex(i);
}
void Finish() {i = 0;}
template <typename T, typename ...Q>
constexpr void Construct(Q &&... params)
{
if constexpr (sizeof...(P) > 0)
{
::new((void *)&static_cast<T &>(self).value) T::type(std::forward<Q>(params)...);
i++; // On a separate line, to be extra sure it doesn't happen if `Construct()` throws.
}
}
};
static constexpr void InOrder(auto &&func)
{
if constexpr (sizeof...(P) > 0)
{
static constexpr auto arr = MakeFuncArray([]<typename T, std::size_t I>
{
return [](decltype((func)) func) {func.template operator()<T, I>();};
});
for (auto x : arr)
x(func);
}
}
public:
constexpr OrderedTuple()
noexcept((std::is_nothrow_default_constructible_v<typename P::type> && ...))
requires(std::is_default_constructible_v<typename P::type> && ...)
{
ConstructorGuard guard(*this);
InOrder([&]<typename T, std::size_t>{guard.template Construct<T>();});
guard.Finish();
}
constexpr OrderedTuple(const OrderedTuple &other)
noexcept((std::is_nothrow_copy_constructible_v<typename P::type> && ...))
requires(std::is_copy_constructible_v<typename P::type> && ...)
{
ConstructorGuard guard(*this);
InOrder([&]<typename T, std::size_t>{guard.template Construct<T>(static_cast<const T &>(other).value);});
guard.Finish();
}
constexpr OrderedTuple(OrderedTuple &&other)
noexcept((std::is_nothrow_move_constructible_v<typename P::type> && ...))
requires(std::is_move_constructible_v<typename P::type> && ...)
{
ConstructorGuard guard(*this);
InOrder([&]<typename T, std::size_t>{guard.template Construct<T>(std::move(static_cast<T &>(other).value));});
guard.Finish();
}
constexpr OrderedTuple &operator=(const OrderedTuple &other)
noexcept((std::is_nothrow_copy_assignable_v<typename P::type> && ...))
requires(std::is_copy_assignable_v<typename P::type> && ...)
{
InOrder([&]<typename T, std::size_t>
{
static_cast<T &>(*this).value = static_cast<const T &>(other).value;
});
return *this;
}
constexpr OrderedTuple &operator=(OrderedTuple &&other)
noexcept((std::is_nothrow_move_assignable_v<typename P::type> && ...))
requires(std::is_move_assignable_v<typename P::type> && ...)
{
InOrder([&]<typename T, std::size_t>
{
static_cast<T &>(*this).value = std::move(static_cast<T &>(other).value);
});
return *this;
}
constexpr ~OrderedTuple() noexcept
{
DestroyFromIndex(sizeof...(P));
}
template <typename ...Q>
constexpr OrderedTuple(Q &&... params)
noexcept((std::is_nothrow_constructible_v<typename P::type, Q &&> && ...))
requires(std::is_constructible_v<typename P::type, Q &&> && ...)
{
ConstructorGuard guard(*this);
InOrder([&]<typename T, std::size_t I>
{
guard.template Construct<T>(std::get<I>(std::forward_as_tuple(std::forward<Q>(params)...)));
});
guard.Finish();
}
template <auto K>
static constexpr bool contains_key = []{
if constexpr (sizeof...(P) == 0)
return false;
else
return requires(OrderedTuple &self){self.DetailGetElem(ValueTag<K>{});};
}();
template <auto K> requires contains_key<K> [[nodiscard]] auto & get() & {return this->DetailGetElem(ValueTag<K>{}) ;}
template <auto K> requires contains_key<K> [[nodiscard]] const auto & get() const & {return this->DetailGetElem(ValueTag<K>{}) ;}
template <auto K> requires contains_key<K> [[nodiscard]] auto &&get() && {return std::move(this->DetailGetElem(ValueTag<K>{}));}
template <auto K> requires contains_key<K> [[nodiscard]] const auto &&get() const && {return std::move(this->DetailGetElem(ValueTag<K>{}));}
};
// ---
#include <iostream>
template <std::size_t N>
struct ConstString
{
char array[N]{};
consteval ConstString(const char (&source)[N])
{
std::copy_n(source, N, array);
}
};
template <ConstString S>
[[nodiscard]] consteval decltype(S) operator""_const()
{
return S;
}
#define MAKE_TEST_CLASS(A, copy_throws_) \
struct A \
{ \
A() {std::cout << #A "()\n";} \
A(const A &) {std::cout << #A "(const " #A " &)\n"; if (copy_throws_) throw 42;} \
A(A &&) {std::cout << #A "(" #A " &&)\n"; if (copy_throws_) throw 42;} \
A &operator=(const A &) {std::cout << #A " &operator=(const " #A " &)\n"; return *this;} \
A &operator=(A &&) {std::cout << #A " &operator=(" #A " &&)\n"; return *this;} \
~A() {std::cout << "~" #A "()\n";} \
};
MAKE_TEST_CLASS(A, false)
MAKE_TEST_CLASS(B, false)
MAKE_TEST_CLASS(C, false)
MAKE_TEST_CLASS(X, true)
int main()
{
{
OrderedTuple<
OrderedTupleElem<"foo"_const, 1, A>,
OrderedTupleElem<"bar"_const, 2, B>,
OrderedTupleElem<"baz"_const, 0, C>
> x, y(x), z(std::move(x)), w(A(), B(), C{});
x = y;
x = std::move(y);
(void)x.get<"foo"_const>();
(void)x.get<"bar"_const>();
(void)x.get<"baz"_const>();
OrderedTuple<> empty1, empty2(empty1), empty3(std::move(empty1));
empty1 = empty2;
empty1 = std::move(empty2);
}
// Check partial destruction:
try
{
OrderedTuple<
OrderedTupleElem<"a"_const, 2, A>,
OrderedTupleElem<"b"_const, 1, X>,
OrderedTupleElem<"c"_const, 0, C>
> foo(A(), X(), C{});
}
catch (...) {}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
139 次 |
最近记录: |