alignas
Controls the alignment requirement of a type or variable declaration, overriding the compiler's default to meet hardware, ABI, or concurrency constraints.
alignassince C++11A declaration specifier that sets the alignment requirement of a type or variable to a specified power-of-two boundary, which must be at least as strict as its natural alignment.
Overview
Every type has a natural alignment β the byte boundary to which the CPU requires (or prefers) its objects to be addressed. alignas lets you strengthen that requirement, forcing a larger boundary than the compiler would choose by default. It cannot be used to weaken alignment; attempting to do so makes the program ill-formed.
Alignment control matters in several concrete scenarios:
- SIMD and vectorisation: SSE/AVX load instructions require 16- or 32-byte aligned data. Misaligned loads produce a fault on strict-alignment ISAs or a measurable performance penalty on x86.
- Cache-line isolation: Two frequently written fields sharing a cache line causes false sharing between threads. Writes from one thread invalidate the entire line for other cores, forcing spurious reloads even when threads never access the same variable.
- Overaligned storage buffers: Reserving raw bytes for placement
newrequires the buffer's alignment to match the target type's. - Hardware-mapped regions: Some peripheral or DMA registers mandate specific alignment for correct bus transactions.
In C++17, ::operator new gained overloads for over-aligned types, so dynamically allocated over-aligned objects are handled correctly without manual allocator wiring. In C++14 and earlier, standard allocation only guarantees alignof(std::max_align_t) β typically 16 bytes.
Syntax
alignas(expression) // integral constant; 0 or a valid power-of-2 alignment
alignas(type-id) // equivalent to alignas(alignof(type-id))
alignas(pack...) // C++11: expands to one alignas per pack element; takes the maxalignas is permitted on:
- A class or struct definition.
- A non-bitfield data member declaration.
- A variable declaration.
It is not permitted on a function parameter or the exception parameter of a catch clause.
When multiple alignas specifiers appear on the same declaration, the effective alignment is the largest non-zero value among them. alignas(0) is always silently ignored.
Weakening natural alignment is ill-formed:
struct alignas(8) S {};
struct alignas(1) U { S s; }; // error: alignas(1) would weaken S's alignmentExamples
Aligning a struct for SIMD
#include <cassert>
struct alignas(32) Vec8f { // 32-byte boundary required for AVX _mm256_load_ps
float data[8];
};
static_assert(alignof(Vec8f) == 32);
static_assert(sizeof(Vec8f) == 32);
void process(const Vec8f* v) {
// Safe to use _mm256_load_ps(v->data) β alignment guaranteed by type
(void)v;
}Aligning individual variables
alignas on a variable overrides alignment for that object only, independent of the type's natural alignment:
#include <cstdint>
alignas(64) int hot_counter = 0; // occupies its own cache line
alignas(alignof(double)) char scratch[8]; // usable as double-aligned storage
// alignof reports the type's alignment, not the variable's.
// hot_counter has type int (alignof == 4), but &hot_counter is 64-byte aligned.
static_assert(alignof(int) == 4);Eliminating false sharing (C++17)
#include <new> // std::hardware_destructive_interference_size β C++17
struct ThreadCounters {
alignas(std::hardware_destructive_interference_size) long writer_count{0};
alignas(std::hardware_destructive_interference_size) long reader_count{0};
};Without alignas, both fields likely share a 64-byte cache line. A write from the writer thread marks the entire line dirty, stalling the reader thread on its next access β even though neither thread ever touches the other's variable.
When targeting C++14 or earlier where the constant is unavailable, use a conservative fixed value:
constexpr std::size_t k_cache_line = 64; // portable conservative assumption
struct ThreadCounters {
alignas(k_cache_line) long writer_count{0};
alignas(k_cache_line) long reader_count{0};
};Aligned storage for placement new
#include <new> // std::launder β C++17
#include <utility>
template <typename T>
class InlineOptional {
alignas(T) unsigned char storage_[sizeof(T)];
bool engaged_ = false;
public:
template <typename... Args>
void emplace(Args&&... args) {
::new (storage_) T(std::forward<Args>(args)...);
engaged_ = true;
}
T& value() {
// std::launder required in C++17+ after placement new through a char buffer
return *std::launder(reinterpret_cast<T*>(storage_));
}
~InlineOptional() {
if (engaged_) value().~T();
}
};alignas(T) guarantees storage_ carries alignof(T) alignment, so the placement new produces a correctly aligned object. Without it the buffer has alignment 1 β undefined behaviour for any type with stricter requirements.
Parameter pack expansion
alignas accepts a type or non-type parameter pack, expanding to one specifier per element with the maximum winning:
#include <algorithm>
template <typename... Ts>
struct AlignedUnion {
alignas(Ts...) unsigned char storage[std::max({sizeof(Ts)...})];
};
using U = AlignedUnion<int, double, char>;
static_assert(alignof(U) == alignof(double)); // double has the strictest alignmentBest Practices
Prefer alignas(T) over hard-coded numbers when matching a type's alignment. If T gains a wider member in a later revision, the specifier stays correct automatically.
Use std::hardware_destructive_interference_size for cache-line separation (C++17). The constant is target-specific; hard-coding 64 is incorrect on architectures with larger lines and may be unnecessarily wasteful on those with smaller ones.
Audit dynamic allocation in pre-C++17 code. In C++14, std::make_unique<Vec8f>() invokes the non-aligned operator new and may return memory that is insufficiently aligned. Either upgrade to C++17 or use aligned_alloc / platform allocators directly.
Verify actual address alignment at runtime during debugging, not via alignof. alignof reports the type's requirement; inspecting reinterpret_cast<uintptr_t>(ptr) % alignment == 0 confirms the live address.
Common Pitfalls
Attempting to weaken alignment. Specifying a smaller alignment than a member or base class requires is ill-formed. The diagnostic may cite an opaque ABI constraint rather than identifying the offending member, making the error non-obvious in deeply nested structures.
alignof does not reflect alignas on a variable.
alignas(64) int x;
static_assert(alignof(decltype(x)) == 4); // true: alignof sees the type, not the var
// runtime address of x is 64-byte aligned despite the aboveCode that reads alignof(T) to decide whether a cast is safe may draw the wrong conclusion when the storage was declared with a stricter alignas.
alignas on function parameters is ignored or ill-formed. The standard does not permit it; some compilers accept it silently and ignore it, others diagnose it. Pass over-aligned objects by pointer or reference:
void bad(alignas(32) Vec8f v); // ill-formed; compiler may not preserve alignment
void good(const Vec8f& v); // reference; alignment of the caller's object is keptOver-aligned types in std::vector before C++17. std::allocator<T> before C++17 calls the unaligned operator new, so std::vector<Vec8f> may contain misaligned elements. In C++17 this is fixed automatically. In earlier standards, supply a custom allocator that calls aligned_alloc.
See Also
alignofβ queries the alignment requirement of a type at compile time- Attributes β the broader set of
[[...]]declaration modifiers std::alignβ adjusts a pointer to meet an alignment requirement within a raw bufferstd::hardware_destructive_interference_sizeβ portable cache-line size constant (C++17)