Передача параметров в шаблонах по значению и по ссылке

76
12 апреля 2021, 20:50

Пишу шаблон функции:

template <class T> void fun(T tt){
//здесь какая-то работа с tt
}

Теперь хочу, чтобы если тип T маленький, то чтобы при инстанцировании параметр передавался по значению:

void fun(little_struct tt){
//здесь какая-то работа с tt
}

А если если тип T большой, то чтобы при инстанцировании параметр передавался по ссылке:

void fun(big_struct& tt){
//здесь какая-то работа с tt
}

Вопросы:

  1. Можно ли так сделать в С++ (чтобы транслятор сам определял размер параметра и соответственно инстанцировал шаблон).
  2. Если можно, то как? И как тогда указать критерий для размера параметра, после которого параметр будет передаваться по ссылке?
Answer 1

Вариант с одной функцией

template<class T>
void foo(typename std::conditional_t<(sizeof(T) <= sizeof(void*)) && std::is_trivially_copyable_v<T>, T, const T&> arg)
{
}

Но так компилятор не может вывести T. Мне на ум не приходит как это разрешить при помощи шаблонов. Так что могу предложить вам писать

foo<Type>(arg)

или сделать макрос

#define foo(arg) foo_impl<decltype(arg)>(arg)
Answer 2

Так не годится?

template<typename T, typename = enable_if_t<sizeof(T) <= 2>>
void f(T)
{
    cout << "By value\n";
};

template<typename T, typename = enable_if_t<(sizeof(T) > 2)>>
void f(const T&)
{
    cout << "By reference\n";
};
int main(int argc, const char * argv[])
{
    f(short(3));
    f(3);
}

Можно поиграться в игру conditional

template<typename T>
    void f(typename conditional_t<sizeof(T) <= 2,T,const T&> t)
{
    if (is_reference_v<decltype(t)>)
        cout << "By reference\n";
    else
        cout << "By value\n";
};

но тогда надо явно указывать тип при вызове -

f<short>(3);
f<int>(3);

Что-то в голове вертится, как обойти... но никак не доверчивается :), а другой работы хватает. Если подсознание само решит проблему :) - допишу.

Answer 3

Нельзя менять тип и при этом сохранять deducible context (т.е. не указывать тип при вызове). Но в С++ почти всё, что не решается напрямую, решается косвенным образом. Поэтому нужно просто ввести один уровень косвенности. Вашу функцию с кодом мы немного переименуем и будет она выглядеть так:

template <class T> void fun_impl(T tt) 
{
    //...
}

А теперь напишем вспомогательную функцию, которая за нас сделает всю необходимую работу:

template <class T> void fun(T&& tt)
{
    using Pure_t = std::decay_t<T>;
    constexpr bool isBig = sizeof(T) > 16;
    if constexpr(isBig)
        fun_impl<Pure_t&>(tt);
    else
        fun_impl<Pure_t>(tt);
}

Вот и всё. Можно добавить ещё пачку булевых переменных, чтобы охватить вектора, строки и прочее — всё это добавляется элементарно.

P.S. Я надеюсь, что интерес тут сугубо академический, потому что в подобном разграничении нет никакого смысла. Подобным функциям не место в реальном коде.

READ ALSO
Умножение матрицы на поинт

Умножение матрицы на поинт

Имеется матрица (допустим 3x3) которая применяется для преобразования некоего массива двумерных точек (допустим std::vector<Point>)Как это грамотно...

97
Ошибка парсинага ConfigParser

Ошибка парсинага ConfigParser

Есть проблема определения команд СonfigParser'омВыполнял следующую последовательность действий:

79
Удаление функций C++

Удаление функций C++

Если функция является зарезервированным набором команд в памяти, то могу ли я её удалить? Если да, то как?

66
HTML: готовый список шрифтов

HTML: готовый список шрифтов

Есть ли готовый список шрифтов для html страницы, что бы можно было подключить в несколько строчек кода? Под список шрифтов я имею ввиду выпадающий...

83