std :: get like (частичная) специализация шаблона - PullRequest
0 голосов
/ 28 августа 2018

У меня есть класс для комплексных чисел :

template<typename T>
struct Complex{
    T r;
    T i;
};

Я решил добавить функцию, аналогичную std::get:

template<int X, typename T>
T get(const Complex<T> &a){
   switch(X){
       case 0: return a.r;
       case 1: return a.i;
   }
}

Это работает нормально. Также я знаю, что компилятор может оптимизировать его.

Тогда я решил переписать это по-другому:

template<int X,typename T>
T get(const Complex<T> &a);

template<typename T>
constexpr T get<0, T>(const Complex<T> &a){
    return a.r;
}

template<typename T>
constexpr T get<1, T>(const Complex<T> &a){
    return a.i;
}

Однако это не компилируется, и мне любопытно, насколько правильной будет реализация?

Я пытался проверить, как создается std::get, но я не смог найти ничего, что было бы "читабельным".

Ответы [ 4 ]

0 голосов
/ 28 августа 2018

Шаблоны функций не могут быть частично специализированными.

Другим методом является диспетчеризация тегов , который достигает аналогичного эффекта при перегрузке функции:

template<int X>
using Int = std::integral_constant<int, X>;

template<typename T> inline T get(const Complex<T> &a, Int<0>) { return a.r; }
template<typename T> inline T get(const Complex<T> &a, Int<1>) { return a.i; }

template<int X, typename T>
inline T get(const Complex<T> &a) { return get(a, Int<X>{}); }
0 голосов
/ 28 августа 2018

Проблема в том, что вы не можете частично специализировать шаблоны функций. Пожалуйста, прочитайте C ++ шаблонная специализация функции и Получение «недопустимого использования явных аргументов шаблона» при выполнении частичной специализации указателя для метода класса , который Ник описывает то же решение.

Чтобы заставить работать специализацию шаблона, см. Ответ Ника.

Хотя ваше решение кажется хорошим Я бы просто поменял переключатель на if constexpr.

#include <iostream>

template<typename T>
struct Complex 
{
    T r;
    T i;
};

template<int X, typename T>
constexpr T get(const Complex<T> &a)
{
    if constexpr(X == 0)
    {
        return a.r;
    }
    else if constexpr (X == 1)
    {
        return a.i;
    }
}

int main()
{
    Complex<int> test;
    test.r = 1;
    test.i = 12;

    std::cout << get<0>(test) << std::endl;
    std::cout << get<1>(test) << std::endl;

    std::cin.get();

}
0 голосов
/ 28 августа 2018

В C ++ 11 вы можете реализовать это упражнение следующим образом:

#include <type_traits>

template<typename T>
struct Complex{
    T r;
    T i;
};


template<int X, typename T>
constexpr typename std::enable_if<X == 0,T>::type
get(const Complex<T> &a){
    return a.r;
}

template<int X, typename T>
constexpr typename std::enable_if<X == 1,T>::type
get(const Complex<T> &a){
    return a.i;
}

Живая демоверсия

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

В C ++ 14 вы можете написать более лаконичный код, используя std::enable_if_t.

А в C ++ 17 вы можете использовать if constexpr для написания одного шаблона функции вместо SFINAE перегружает.

0 голосов
/ 28 августа 2018

Мне удалось придумать это, но это выглядит очень сложно для такой простой задачи:

namespace complex_impl__{
    template<int X, typename T>
    struct GetHelper{
        static T get(const Complex<T> &a);
    };

    template<typename T>
    struct GetHelper<0, T>{
        constexpr static T get(const Complex<T> &a){
            return a.r;
        }
    };

    template<typename T>
    struct GetHelper<1, T>{
        constexpr static T get(const Complex<T> &a){
            return a.i;
        }
    };
}

template<int I,typename T>
constexpr T get(const Complex<T> &a){
    return complex_impl__::GetHelper<I, T>::get(a);
}

Тогда я смог перевести T в get() метод, чтобы я мог сделать меньше кода:

namespace complex_impl__{
    template<int I>
    struct GetHelper{
        template<typename T>
        static T get(const Complex<T> &a);
    };

    template<>
    struct GetHelper<0>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.r;
        }
    };

    template<>
    struct GetHelper<1>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.i;
        }
    };
}

template<int I,typename T>
constexpr T get(const Complex<T> &a){
    return complex_impl__::GetHelper<I>::get(a);
}

Можно «раздеть» весь template<int I> struct GetHelper класс:

namespace complex_impl__{
    template<int I>
    struct GetHelper;

    template<>
    struct GetHelper<0>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.r;
        }
    };

    template<>
    struct GetHelper<1>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.i;
        }
    };
}

template<int I,typename T>
constexpr T get(const Complex<T> &a){
    return complex_impl__::GetHelper<I>::get(a);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...