Специализация шаблона C++ STD внутри другого пространства имен

Я решил эту проблему, которая требовала от меня группировки анаграмм. Таким образом, для данного ввода он должен дать следующий результат:

Ввод: strs = ["eat","tea","tan","ate","nat","bat"]

Вывод: [["летучая мышь"],["нат","загар"],["ели","есть","чай"]]

Я решил использовать класс Signature для хранения частоты символов для каждого слова вместе с простым алгоритмом хеширования.

#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <array>
#include <list>
#include <string>

template <class T> inline void hash_combine( std::size_t& seed, const T& v ) 
{
    std::hash<T> hasher;
    seed ^= hasher( v ) + 0x9e3779b9 + ( seed << 6 ) + ( seed >> 2 );
}

class Signature 
{
public:
    void Push( char c ) 
    {
        ++counts[c - 'a']; 
    }
    bool operator==( const Signature& rhs ) const 
    { 
        return counts == rhs.counts; 
    }
    size_t Hash() const 
    {
        size_t hash = 0;
        for( auto v : counts ) 
        {
            hash_combine( hash, v );
        }

        return hash;
    }

private:
    std::array<unsigned char, 26> counts = { 0 };
};

Затем я использую эту специализацию шаблона:

namespace std 
{
    template <> struct std::hash<Signature> 
    {
        size_t operator()( const Signature& s ) const 
        { 
            return s.Hash(); 
        }
    };
}

За ним следует класс AnagramMap, который имеет неупорядоченную карту подписей.

class AnagramMap 
{
public:
    void Push( const Signature& sig, int index ) 
    {
        auto val = map.emplace( sig, std::list{ index } );
        if ( !val.second ) 
        {
            val.first->second.emplace_back( index );
        }
    }
    size_t GetSize() 
    { 
        return map.size(); 
    }
    auto begin() 
    { 
        return map.begin(); 
    }
    auto end() 
    { 
        return map.end(); 
    }

private:
    std::unordered_map<Signature, std::list<int>> map;
};

Чтобы завершить это, класс Solution использует классы Signature и AnagramMap для решения проблемы и возврата правильно сгруппированных анаграмм:

class Solution 
{
public:
    std::vector<std::vector<std::string>> groupAnagrams( std::vector<std::string>& strs )
    {
        AnagramMap anagrams;

        for( auto i = 0; i < strs.size(); ++i )
        {
            Signature sig;
            for( auto c : strs[i] )
            {
                sig.Push( c );
            }

            anagrams.Push( sig, i );
        }

        std::vector<std::vector<std::string>> result;

        for( auto& anagram : anagrams )
        {
            auto current_list = anagram.second;
            std::vector<std::string> current_anagram_group;

            while( !current_list.empty() )
            {
                auto current_anagram = strs[current_list.front()];
                current_list.pop_front();
                current_anagram_group.emplace_back( current_anagram );
            }
            result.emplace_back( current_anagram_group );
        }

        return result;
    }
};

Все работает нормально, но теперь я хотел переместить классы Signature и AnagramMap в новое пространство имен, но не класс Solution, примерно так:

namespace foo
{
    class Signature { ... }
    class AnagramMap { ... }
    
}

Есть ли способ добиться этого?

Явная специализация может быть объявлена ​​в любой области, где может быть определен ее основной шаблон (которая может отличаться от области, в которой определен основной шаблон; например, при специализации вне класса шаблона члена). Явная специализация должна появиться после объявления неспециализированного шаблона. en.cppreference.com/w/cpp/language/template_specialization

Gene 28.08.2024 03:06

«Ненависть приводит к страданию» — Йода.

user4581301 28.08.2024 03:17
namespace std { template <> struct std::hash<Signature> ... -- дополнительная квалификация не допускается. Вы либо специализируетесь на заключении пространства имен, то есть глобальном, написании struct std::hash..., либо на стандартном пространстве имен без квалификации hash
Gene 28.08.2024 03:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам не следует специализироваться std::hash на namespace std. Вам следует специализироваться std::hash на глобальном пространстве имен. Когда вы используете struct std::hash, вы уже специализируетесь на namespace std для определенного пользователем типа.

Поскольку Signature находится в глобальном пространстве имен, специализация должна быть

// DELETE namespace std 
// DELETE {
template <> struct std::hash<Signature> 
{
    size_t operator()( const Signature& s ) const 
    { 
        return s.Hash(); 
    }
};
// DELETE }

https://godbolt.org/z/v46G34GTq - хорошо компилируется в namespace foo.

Поиск, зависящий от аргументов, не имеет к этому никакого отношения. Сопоставление специализаций просто учитывает все предыдущие явные и частичные объявления специализации того же основного шаблона, независимо от того, где эти объявления находились. Почему вы говорите «вы не должны» специализироваться на namespace std?

aschepler 28.08.2024 03:17

Другие вопросы по теме

Почему определение оператора = (а не объявление) должно быть написано, когда подходящий шаблон легко доступен
Использование SFINAE в конструкторе, чтобы проверить, существует ли конструктор типа члена
Использование if-constexpr и концепций для обнаружения экземпляра определенного типа расширенной политики
Почему квалификатор const игнорируется при применении к выведенной ссылке Lvalue в C++?
Создание декартова произведения на основе аргумента шаблона целочисленного диапазона
Почему выведение std::call_once не удалось и возникла ошибка «не удалось вывести параметр шаблона ‘_Callable’»
Невозможно получить доступ к специализации класса шаблона через универсальную ссылку
Вывести класс шаблона с уменьшенным количеством параметров шаблона
Как добавить собственный класс CSS в навигационное меню WooCommerce MyAccount?
Руководства по выводу кортежей в C++17 (CTAD): неявно генерируемые и определяемые пользователем