Почему GCC считает, что параметр шаблона является int, тогда как это совершенно другой тип?

У меня возникла проблема с компиляцией следующей программы с использованием GCC (я пробовал множество версий, все сбой с той же ошибкой). Он прекрасно компилируется в Clang:

#include <vector>

struct Tag1
{
    static void logAllocation(){}
    static void logDeallocation(){}
};
struct Tag2
{
    static void logAllocation(){}
    static void logDeallocation(){}
};

template<typename Tag, typename T>
struct MyAllocator
{
    using value_type = typename std::allocator<T>::value_type;

    T* allocate(std::size_t n)
    {
        Tag::logAllocation();
        return std::allocator<T>{}.allocate(n);
    }

    void deallocate(T* p, std::size_t n)
    {
        Tag::logDeallocation();
        std::allocator<T>{}.deallocate(p, n);
    }
};

int main()
{
    std::vector<int, MyAllocator<Tag1, int>> vec;
}

Проблема в том, что GCC считает, что Tag==int внутри MyAllocator и я получаю сообщение об ошибке, которое 'logDeallocation' не является членом 'int'. Это ошибка в GCC? Когда я переворачиваю параметры шаблона (template<typename T, typename Tag) и объявляю свой вектор как std::vector<int, MyAllocator<int, Tag1>> vec; он компилируется.

Ответы

Ответ 1

Это не подходящий распределитель, а поставка его как компонента библиотеки приводит к неопределенному поведению (таким образом, обе реализации соответствуют). Вы отсутствуете !=, ==, кросс-тип неявного преобразования, и, как уместно здесь, rebind.

allocator_traits default rebind implementation предполагает, что тип значения является первым параметром шаблона (и что любые оставшиеся параметры шаблона могут быть повторно использованы без изменений). Так как это не так для вашего распределителя, вам нужно либо предоставить свою собственную rebind либо инвертировать порядок параметров шаблона.


vector довольно необычен тем, что реализация может, если захочет, просто использовать поставляемый распределитель как -я без перезаписи. Вот почему ваш пример кода компилируется с помощью libc++. Контейнеры libstdc++ поддерживают расширение, которое позволяет вам выполнять vector<int, allocator<char>>, поэтому он всегда переустанавливает распределитель на указанный value_type.