Универсальные ссылки (т.е. "прямые ссылки", стандартное имя c++
) и безупречная переадресация в c++11
, c++14
, и далее имеют много важных преимуществ; см. здесь и здесь.
В статье Скотта Мейерса, упомянутой выше (ссылка), в качестве эмпирического правила указано, что:
Если переменная или параметр объявлены как имеющие тип T && для некоторого выведенного типа T, эта переменная или параметр является универсальной ссылкой.
Пример 1
В самом деле, используя clang++, мы видим, что следующий фрагмент кода успешно скомпилируется с помощью -std=c++14
:
#include <utility>
template <typename T>
decltype(auto) f(T && t)
{
return std::forward<T>(t);
}
int x1 = 1;
int const x2 = 1;
int& x3 = x1;
int const& x4 = x2;
// all calls to `f` result in a successful
// binding of T&& to the required types
auto r1 = f (x1); // various lvalues okay, as expected
auto r2 = f (x2); // ...
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (int()); // rvalues okay, as expected
Учитывая любое описание универсальных ссылок (прямых ссылок) и вывода типа (см., например, это объяснение), понятно, почему выше работ. Хотя, по тому же объяснению, не совсем понятно, почему нижеследующее не работает.
(не удалось) Пример 2
Этот вопрос касается той же проблемы. Однако предоставленные ответы не объясняют, почему шаблонные типы не классифицируются как "выведенные".
То, что я собираюсь показать (по-видимому), удовлетворяет требованию, указанному выше Мейерсом. Однако следующий код обрезал сбой для компиляции, вызвав ошибку (среди прочего, для каждого вызова f
):
test.cpp: 23: 11: ошибка: нет соответствующей функции для вызова 'f'
auto r1 = f (x1);
test.cpp: 5: 16: note: функция-кандидат [с T = foo, A = int] not жизнеспособный: никакого известного преобразования из 'struct foo <int> 'to' foo <int> & & для 1-го аргумента
decltype (auto) f (T < A && t)
#include <utility>
//
// It **seems** that the templated type T<A> should
// behave the same as an bare type T with respect to
// universal references, but this is not the case.
//
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t)
{
return std::forward<T<A>> (t);
}
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` **fail** to compile due
// to **unsuccessful** binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (foo<int> {1}); // only rvalue works
В контексте, поскольку вывод T<A>
параметра f
, конечно, объявление параметра T<A>&& t
будет вести себя как универсальная ссылка (прямая ссылка).
Пример 3 (для ясности при описании проблемы)
Позвольте мне подчеркнуть следующее: сбой кода в Example 2
для компиляции - не из-за того, что struct foo<>
является шаблоном. Ошибка, по-видимому, является причиной только объявлением параметра f
в качестве шаблонного типа.
Рассмотрим следующий вариант предыдущего кода, который теперь выполняет:
#include <utility>
//
// If we re-declare `f` as before, where `T` is no longer a
// templated type parameter, our code works once more.
//
template <typename T>
decltype(auto) f (T && t)
{
return std::forward<T> (t);
}
//
// Notice, `struct foo<>` is **still** a templated type.
//
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` (again) result in
// a successful binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
Для меня удивительно, что это простое изменение полностью изменяет поведение вывода типа для параметра типа шаблона f
.
Вопросы:
Почему второй пример работает не так, как ожидалось? Существуют ли методы преодоления этой проблемы с шаблонами в c++11/14
? Существуют ли хорошо известные, существующие кодовые базы (в дикой природе), успешно использующие прямые ссылки c++
с шаблонами?