Новый оператор перегрузки с меньшим выравниванием по умолчанию - PullRequest
7 голосов
/ 22 июня 2019

C ++ 17 введено Динамическое выделение памяти для выровненных данных

Помимо существующего std::max_align_t фундаментального выравнивания, оно добавило __STDCPP_DEFAULT_NEW_ALIGNMENT__ минимальное выравнивание, которое операторновые гарантии.

При 64-битной компиляции MSVC2017 эти константы приводят к std::max_align_t размера 8 и __STDCPP_DEFAULT_NEW_ALIGNMENT__ размера 16.

Однако разрешено отменять оператор new / free, как упоминалось в cppreference: operator new - глобальные замены .

Глядя на все документы, мне неясно, разрешено ли этой функции предоставлять новое выравнивание по умолчанию, и если да,если нам разрешено переопределить эту константу.

Пример для иллюстрации:

#include <new>
#include <iostream>
#include <cassert>
#include <cstdint>
#include <cstddef>

static_assert(alignof(std::max_align_t) == 8);
static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ == 16);


void * operator new(size_t size) 
{ 
    std::cout << "New operator overloading " << std::endl; 
    void * p = std::malloc((size == 8) ? 16 : size); 
    assert(std::uintptr_t(p)%16 == 0);
    if (size == 8)
        {
        auto correctedPtr = std::uintptr_t(p) + 8;
        return (void*)correctedPtr;
        }
    return p; 
} 

void operator delete(void * p) 
{ 
    std::cout << "Delete operator overloading " << std::endl; 
    if (std::uintptr_t(p)%16 != 0)
    {
        auto correctedPtr = std::uintptr_t(p) - 8;
        std::free((void*)correctedPtr);
    }
    std::free(p); 
}

namespace
{
    struct D
    {
        double d;
    };
}


int main(int, char**)
{
    new D{};
    return 0;
}

Код в проводнике компилятора

Причина, по которой явопрос в том, что я расследую сбои в программе MSVC, которая сейчас компилируется с Clang.Здесь мы заметили, что clang использует инструкции процессора, которые полагаются на это 16-битное выравнивание, чтобы инициализировать класс размера 8.

1 Ответ

5 голосов
/ 22 июня 2019

Согласно N4659 (последний открытый проект для C ++ 17):

6.7.4p3:

Любые функции выделения и / или освобождения, определенные в программе на C ++, включая версии по умолчанию в библиотеке, должны соответствовать семантика, указанная в 6.7.4.1 и 6.7.4.2.

6.7.4.1p2:

... Возвращаемый указатель должен быть соответствующим образом выровнен, чтобы его можно было преобразован в указатель на любой подходящий тип полного объекта (21.6.2.1) а затем используется для доступа к объекту или массиву в хранилище распределяется (до тех пор, пока хранилище не будет явно освобождено при вызове соответствующая функция освобождения). ...

19.8p1:

Следующие имена макросов должны быть определены реализацией: ... __STDCPP_DEFAULT_NEW_ALIGNMENT__ Целочисленный литерал типа std :: size_t, значением которого является выравнивание, гарантированное вызовом operator new(std::size_t) или operator new[](std::size_t). ...

19.8p4:

Если любое из предопределенных имен макросов в этом подпункте или идентификатор defined, является предметом #define или #undef директива предварительной обработки, поведение не определено. ...

Таким образом, вы не можете изменить значение __STDCPP_DEFAULT_NEW_ALIGNMENT__ внутри вашей программы, и если ваша функция выделения вызывается для alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) типа размера 8, вы не можете это обнаружить, но вам все равно нужно вернуть соответствующим образом выровненный указатель.

Тем не менее, вы можете изменить значение __STDCPP_DEFAULT_NEW_ALIGNMENT__, как определено самим clang, используя опцию компилятора -fnew-alignment. Не уверен, поможет ли это в вашем случае.

...