Одинаковая специализация шаблона для константного и неконстантного типа - PullRequest
0 голосов
/ 31 октября 2019

У меня есть следующий код:

#include <iostream>

class A
{};

class B
{};

template<typename T>
void Do(T data)
{
    std::cout << "Do() default\n";
}

template<>
void Do(A* data)
{
    std::cout << "Do(A*)\n";
}

template<>
void Do(B* data)
{
    std::cout << "Do(B*)\n";
}

int main(int argc, char* argv[])
{
    A* a = nullptr;
    B* b = nullptr;

    const A* aConst = nullptr;
    const B* bConst = nullptr;

    Do(a);
    Do(aConst);

    Do(b);
    Do(bConst);

    return 0;
}

, который выводит:

Do(A*)
Do() default
Do(B*)
Do() default

Как мне переписать код, чтобы разделить специализацию шаблона для константного и неконстантного типа без вставки копииспециализация с указателем ключевого слова const, поэтому он производит вывод:

Ответы [ 3 ]

3 голосов
/ 31 октября 2019

Вместо специализации можно перегрузить. Использование

template<typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, A>, bool> = true>
void Do(T* data)
{
    std::cout << "Do(A*)\n";
}

template<typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, B>, bool> = true>
void Do(T* data)
{
    std::cout << "Do(B*)\n";
}

Эти функции будут вызываться при передаче const A* / A* / const B* / B*, так как они лучше соответствуют общему шаблону. Причина, по которой они лучше подходят, заключается в том, что T более ограничен. Он считается более специализированным , и поэтому он выиграет в тай-брейке с общим шаблоном в разрешении перегрузки.

0 голосов
/ 31 октября 2019

C ++ 14

Я проголосовал этот ответ , который является хорошей демонстрацией для SFINAE. Чтобы расширить его, вы можете упростить выражение SFINAE с помощью std::is_convertible. Это более аналогично тому, как будет работать разрешение перегрузки (добавление константного квалификатора).

template<typename T, std::enable_if_t<std::is_convertible_v<T*, const A*>, int> = 0>
void Do(T* data)
{
    std::cout << "Do(A*)\n";
}

template<typename T, std::enable_if_t<std::is_convertible_v<T*, const B*>, int> = 0>
void Do(T* data)
{
    std::cout << "Do(B*)\n";
}

C ++ 17

В качестве бонуса, в C ++ 17 с constexpr ifВы можете использовать одну функцию для всех случаев:

template<typename T>
void Do(T data)
{
    if constexpr (std::is_convertible_v<T, const A*>)
        std::cout << "Do(A*)\n";
    else if constexpr (std::is_convertible_v<T, const B*>)
        std::cout << "Do(B*)\n";
    else    
        std::cout << "Do() default\n";
}
0 голосов
/ 31 октября 2019

Вы можете использовать этот шаблон, если вы хотите написать небольшую заглушку:

template<>
void Do(const A* data)
{
    std::cout << "Do(A*)\n";
}

template<>
void Do(A* data)
{
    Do((const A*)data);
}

Основной код, который вы не хотите дублировать, использует const A*, потому что, ну, вы хотите егоработать с константными данными тоже. Неконстантный просто переходит к нему.

...