Перегрузка функции с помощью шаблонов


34

Я пытаюсь определить функцию, используя шаблоны, и я хочу, чтобы typename было либо int, либо anEnum (конкретное перечисление, которое я определил). Я пробовал следующее, но мне не удалось:

template <int | anEnum T> // or <int T, anEnum T> or <int, anEnum T>
bool isFunction(const T &aVariable){}

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

isFunction(aVariable) // and not isFunction<int> (aVariable) nor isFunction<anEnum> (aVariable)

По сути, я хочу, чтобы эта функция была шаблонной для типов int и aNum. Я искал это, но не смог найти ответ. Что я могу пропустить? Спасибо,


Если это ровно одно перечисление или тип int, почему бы просто не написать обе функции? Зачем вам нужен шаблон в таком случае?
Клаус

А как насчет других типов? Вы хотите вернуться falseдля других типов или хотите не создавать экземпляр функции для других типов.
Фрогатто

@frogatto Нет, возвращаемое значение bool не имеет ничего с типами.
BG

@ Клаус, я попросил изучить альтернативы. Основываясь на текущих ответах, я решил просто определить обе функции.
BG

Ответы:


25

В дополнение к ответу, не относящемуся к C ++ 20, если вы случайно можете использовать C ++ 20 и его conceptsвозможности, я бы предложил вам следующую реализацию:

#include <iostream>
#include <concepts>

enum class MyEnum {
    A,
    B,
    C
};

template <typename T>
concept IntegralOrEnum = std::same_as<MyEnum, T> || std::integral<T>;

template <IntegralOrEnum T>
bool isFunction(T const& aVariable) {
    return true;
}

int main() {
    isFunction(MyEnum::A);
    isFunction(3);
    isFunction("my_string"); // error
    return 0;
}

демонстрация

ОБНОВИТЬ

Согласно комментарию @RichardSmith , вот более масштабируемый и многократно используемый подход:

template <typename T, typename ...U>
concept one_of = (std::is_same_v<T, U> || ...);

template <one_of<int, MyEnum> T>
bool isFunction(T const& aVariable) {
    return true;
}

Для конкретного случая, когда требуется, чтобы тип был одним из двух конкретных типов, что-то вроде этого может работать лучше:template<typename T, typename ...U> concept one_of = (std::is_same_v<T, U> || ...); template<one_of<int, MyEnum> T> bool isFunction(T const& aVariable) {
Ричард Смит

1
@RichardSmith Я также обновил свой ответ. Я считаю это более многоразовым и масштабируемым. Спасибо
NutCracker

21

Есть несколько способов сделать это. Все связаны с использованием type_traitsзаголовка. Например, вы можете статически утверждать для рассматриваемых типов в теле функции.

Или, если вам нужно рассмотреть эту функцию среди других перегрузок, можно использовать метод SFINAE.

template<typename T>
auto isFunction(const T &aVariable) 
  -> std::enable_if_t<std::is_same<T, int>::value || std::is_same<T,anEnum>::value, bool> {
}

Это удалит функцию из набора перегрузки перед вызовом, если типы не совпадают. Но если вам не нужно это поведение, статическое утверждение допускает более понятное сообщение об ошибке для программиста.


3

Как насчет этого решения? Код с функцией будет скомпилирован, если тип T удовлетворяет вашим требованиям. В противном случае статическое утверждение не выполнено.

#include <type_traits>
enum anEnum {
    //
};

template <typename T, bool defined = std::is_same<T, int>::value ||
                                     std::is_same<T, anEnum>::value>
bool isFunction(const T& aVariable)
{
    static_assert(defined, "Invalid specialization");

    bool result = false;
    // Put your code here
    return result;
}

1
Это плохо работает с разрешением перегрузки, если присутствуют другие сигнатуры (например, гипотетические isFunction(std::string_view)). Подпись все равно будет действительным совпадением, но создание экземпляра вызывает ошибку.
LF

Вы можете объявить бесполезные подписи как удаленные: bool isFunction (std :: string_view) = delete;
ixjxk

Я говорю о дополнительных перегрузках. В этом случае эта недопустимая подпись может оказаться точным совпадением (например, для строковых литералов), таким образом блокируя перегрузку.
LF

0

Я улучшил https://stackoverflow.com/a/60271100/12894563 ответ. «Если constexpr» может помочь в этой ситуации:

template <typename T>
struct always_false : std::false_type {};

template <typename T>
bool isFunction(const T& aVariable)
{
    if constexpr(std::is_same_v<T, int> || std::is_same_v<T, anEnum>)
    {
        std::cout << "int\n";
        // put your code here
        return true;
    }
    else
    {
        static_assert(always_false<T>::value, "You should declare non-template function or write if constexpr branch for your type");
        return false;
    }
}

bool isFunction(std::string_view)
{
    std::cout << "std::string_view\n";
    return true;
}

int main()
{
    isFunction(std::string_view("1L"));
    isFunction(1);
    //isFunction(1L); // will produce an error message from static_assert
}

isFunction (1L) потерпит неудачу, потому что нет перегруженной функции или ветки 'if constexpr'.

ОБНОВЛЕНИЕ: исправлено пропущено

template <typename T>
struct always_false : std::false_type {};

https://godbolt.org/z/eh4pVn


static_assert(false, ...)плохо сформированный NDR, даже не будучи использованным. Если вам повезет, ваш компилятор сразу же скажет вам, как это делает Кланг
StoryTeller - Unslander Monica

Большое спасибо за ваш комментарий, я ошибся. Исправлено, godbolt.org/z/eh4pVn
ixjxk
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.