Вывод константной ссылки на l-значение из неконстантной ссылки на l-значение в шаблоне C ++ - PullRequest
5 голосов
/ 07 марта 2019

Предположим, у вас есть следующая пара функций:

void f(const int&) { 
    // Do something, making a copy of the argument.
}

void f(int&&) { 
    // Do the same thing, but moving the argument.
}

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

template<typename T> 
void g(T&&) { 
    // Do something, possibly using std::forward to copy or move the argument.
}

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

#include <iostream>

template<typename T> constexpr char *type = nullptr;
template<> constexpr const char *type<int&> = "int&";
template<> constexpr const char *type<const int&> = "const int&";
template<> constexpr const char *type<int> = "int";

template<typename T> 
void g(T&&) { 
    std::cout << reinterpret_cast<void*>(&g<T>)
              << " = &g<" << type<T> << ">" << std::endl;
}

int main() {
    int i = 0;
    const int& cr = 0;

    g(i);
    g(cr);
    g(0);

    return 0;
}

/*
Prints:

0x100f45080 = &g<int&>
0x100f45100 = &g<const int&>
0x100f45180 = &g<int>
*/

Добавлена ​​третья функция для случая, когда T = int&, которого у нас не было, когда мы использовали нашу не шаблонную функциюf выше.В этом случае нам на самом деле не нужна эта неконстантная справочная версия l-значения функции - учитывая, что f было достаточно для наших первоначальных потребностей - и это увеличивает размер нашего кода, особенно если у нас много шаблонных функцийнаписаны таким образом, что вызывают друг друга.

Есть ли способ написать нашу функцию g выше, чтобы компилятор автоматически выводил T = const int&, когда g(i) вызывается в нашем примере кода?То есть способ, при котором нам не нужно вручную писать g<const int&>(i), но все же получить желаемое поведение.

1 Ответ

3 голосов
/ 07 марта 2019

Субъективно говорить, что «прямые ссылки» («универсальные ссылки») лучше, чем выделенные перегрузки.Конечно, во многих случаях это так, но если вы хотите получить полный контроль, они не будут выполнять всю работу.

Вы можете явно убедиться, что пользователи не передают неконстантные lvalue-ссылки, добавив

    static_assert(!std::is_lvalue_reference<T>::value || std::is_const<typename std::remove_reference<T>::type>::value, "only call g with const argument");

внутри g, но это не всегда хорошее решение.

Или вы делаете то, что делается для vector :: push_back (...) и предоставляете явные перегрузки - но это была ваша отправная точка, см. https://en.cppreference.com/w/cpp/container/vector/push_back.

The 'Правильный ответ зависит только от ваших требований.

Редактировать : предложение @Sjoerd будет выглядеть примерно так:

template <typename T>
class aBitComplicated {
public:
 void func(T&& v) { internal_func(std::forward<T>(v)); }
 void func(const T& v) { internal_func(v); }
private:
 template <typename U>
 void internal_func(U&& v) { /* your universal code*/ }
};

Есть также немного более сложная / сложная версия этого, но это должно бытьсамая простая версия для достижения того, что вы просили.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...