Разрешение перегрузки шаблона функции

Я хотел написать несколько шаблонных функций для обработки каламбура определенным образом, поэтому я придумал эти две функции. Первый берет объект и с помощью каламбура преобразует его в другой. Это гарантирует, что оба типа являются POD и имеют одинаковый размер. Второй предназначался для того, чтобы просто взять любой указатель (как если бы это был void*), но все же проверить, что указатель относится к типу POD. Проблема, с которой я столкнулся, заключается в том, что если я передам неконстантный указатель, вместо него будет использоваться первая функция. Что было бы лучшим способом справиться с этим?

template <class TO, class FROM>
FORCE_INLINE TO punning_cast(const FROM &input)
{
    static_assert(std::is_pod<TO>::value, "TO must be POD");
    static_assert(std::is_pod<FROM>::value, "FROM must be POD");
    static_assert(sizeof(TO) == sizeof(FROM), "TO and FROM must be the same size");

    TO out;
    std::memcpy(&out, &input, sizeof(TO));
    return out;
}

template <class TO, class FROM>
FORCE_INLINE TO punning_cast(const FROM *input)
{
    static_assert(std::is_pod<TO>::value, "TO must be POD");
    static_assert(std::is_pod<FROM>::value, "FROM must be POD");

    TO out;
    std::memcpy(&out, input, sizeof(TO));
    return out;
}

person Chris_F    schedule 19.05.2014    source источник
comment
Возможно, typename std::enable_if<!std::is_pointer<FROM>::value,TO>::type в качестве возвращаемого типа первого из них может оказаться полезным (не то, чтобы я пробовал любой из этого, заметьте).   -  person WhozCraig    schedule 19.05.2014
comment
@WhozCraig Я уже ответил на это   -  person Nikos Athanasiou    schedule 19.05.2014
comment
(просто указываю на это, чтобы не было похоже, что я украл ваш комментарий, чтобы ответить)   -  person Nikos Athanasiou    schedule 19.05.2014


Ответы (2)


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

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

#include <type_traits>

template <class TO, class FROM>
FORCE_INLINE typename enable_if<!is_pointer<FROM>::value, TO>::type
    punning_cast(const FROM &input) { ... }

template <class TO, class FROM>
FORCE_INLINE typename enable_if<is_pointer<FROM>::value, TO>::type
    punning_cast(const FROM input) { ... }

Таким образом, примером устранения неоднозначности (между ссылкой и указателем) может быть этот

person Nikos Athanasiou    schedule 19.05.2014
comment
Я не уверен, что шаблоны функций не перегружаются, полностью точен, но +1 в остальном. - person Mooing Duck; 20.05.2014
comment
@MooingDuck Слишком сильное упрощение? Я собирался перейти к разрешению перегрузки шаблона функции, но тогда мне пришлось бы выйти за рамки вопроса (вы считаете Я должен удалить его, чтобы предотвратить неправильное толкование? ) - person Nikos Athanasiou; 20.05.2014
comment
Я думаю, что я бы просто изменил его на что-то вроде странного сочетания шаблонов функций с перегрузкой, см. ссылка. - person Mooing Duck; 20.05.2014
comment
@MooingDuck Хорошая мысль, так и сделаю. Thnx (языковые юристы, вероятно, будут кричать о том, что экземпляры шаблона функции являются теми, которые участвуют в процессе перегрузки, но эта формулировка также позволяет избежать этого;) - person Nikos Athanasiou; 20.05.2014
comment
Это выглядит интересно, однако при использовании этого я столкнулся с другой проблемой. Если я передаю массив, я получаю ошибку о неоднозначной перегрузке. - person Chris_F; 20.05.2014
comment
@Chris_F Массив не является указателем. При передаче в шаблон функции можно вывести его точный тип, предотвращая его распад. Вы можете проверить здесь явные способы преобразования массива в указатель (я добавил метод kloffy для полноты, остальные решений до С++ 14) - person Nikos Athanasiou; 20.05.2014
comment
Должен ли я просто добавить && !std::is_array<FROM>::value к первой версии, чтобы вместо этого заставить версии указателя перехватывать его? - person Chris_F; 20.05.2014
comment
@Chris_F Это дело вкуса. Обратите внимание, что для того, чтобы это работало, вторая версия должна принимать свой аргумент по значению, а не по ref/ptr (как я это пишу), иначе он также будет отключен, и программа не будет компилироваться. - person Nikos Athanasiou; 20.05.2014

Отказ от ответственности: мне больше нравится ответ Никоса Афанасиу для C++11 и далее.

Одним из решений этой (распространенной) проблемы является помещение функций в структуры и добавление вспомогательной функции, которая выбирает одну из них:

template <class TO, class FROM>
struct punning_cast_impl
{
    static FORCE_INLINE TO cast(const FROM &input)
    {
        static_assert(std::is_pod<TO>::value, "TO must be POD");
        static_assert(std::is_pod<FROM>::value, "FROM must be POD");
        static_assert(sizeof(TO) == sizeof(FROM), "TO and FROM must be the same size");

        TO out;
        std::memcpy(&out, &input, sizeof(TO));
        return out;
    }
};

template <class TO, class FROM>
struct punning_cast_impl<TO, FROM*>
{
    static FORCE_INLINE TO cast(const FROM *input)
    {
        static_assert(std::is_pod<TO>::value, "TO must be POD");
        static_assert(std::is_pod<FROM>::value, "FROM must be POD");

        TO out;
        std::memcpy(&out, input, sizeof(TO));
        return out;
    }
};

template<class TO, class FROM>
TO FORCE_INLINE punning_cast(const FROM& input)
{
    return punning_cast_impl<TO, FROM>::cast(input);
}

int main()
{
    double d1 = 50.0;
    int64_t i1 = punning_cast<int64_t>(d1); // calls version #1

    double d2 = 100.0;
    int64_t i2 = punning_cast<int64_t>(&d2); // calls version #2
}
person dlf    schedule 19.05.2014
comment
+1 ты меня опередил. Я бы добавил const в качестве формального аргумента к функции punninc_cast_impl::cast. В противном случае указатель на const нельзя было бы использовать в качестве фактического аргумента. - person Cheers and hth. - Alf; 19.05.2014
comment
@Cheersandhth.-Альф Упс; потерял это как-то при копировании. Спасибо. - person dlf; 19.05.2014
comment
+1 За спортивное поведение. Хотя этот ответ длиннее, он хорошо масштабируется, и выбор может быть сделан только в зависимости от имеющейся проблемы. Хорошо сделано. - person Nikos Athanasiou; 20.05.2014