Перевантаження функції за допомогою шаблонів


34

Я намагаюся визначити функцію за допомогою шаблонів, і я хочу, щоб ім'я типу було або 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. Я шукав це, але не зміг знайти відповіді. Що я можу бракувати? Дякую,


Якщо це саме один enum або type int, чому б просто не написати обидві функції? Навіщо вам потрібен шаблон у такому випадку?
Клаус

Що з іншими типами? Ви хочете повернутися falseдля інших типів чи не бажаєте інстанціювати функцію для інших типів.
жаба

@frogatto Ні, значення віддачі bool не має нічого з типами.
bg

@Klaus Я попросив вивчити альтернативи. На основі поточних відповідей я вирішив просто визначити обидві функції.
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) = видалити;
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) вийде з ладу, оскільки немає перевантаженої функції або гілки "якщо constexpr".

ОНОВЛЕННЯ: Виправлена ​​помилка

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

https://godbolt.org/z/eh4pVn


static_assert(false, ...)є неправильно сформованою НДР, навіть не використовуючись. Якщо вам пощастить, ваш компілятор скаже вам відразу, як це робить Кланг, godbolt.org/z/m_Gk9n
StoryTeller - Невідповідна Моніка

Дякую за ваш коментар, я помилився. Виправлено, godbolt.org/z/eh4pVn
ixjxk
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.