Рассмотрим обычную совершенную переадресацию:
class Test
{
public:
Test() = default;
Test(Test const&) { std::cout << "copy\n"; }
Test(Test&&) { std::cout << "move\n"; }
};
void test(Test)
{ }
template <typename T>
void f(T&& t)
{
test(std::forward<T>(t));
}
int main()
{
std::cout << "expect: copy\n";
Test t;
f(t);
std::cout << "expect: move\n";
f(Test());
return 0;
}
Пока все в порядке. Но если мы теперь введем указатель на функцию, у меня возникнет проблема, что я не могу объявить универсальный (пересылка) ссылки:
decltype(&f<Test>) ptr = &f<Test>;
// above produces ordinary r-value references
// making below fail already on compilation:
ptr(t);
Указатели функций шаблона также становятся проблематичными:
template <typename T>
void(*ptr)(T&& t) = &f<T>; // again resolved to r-value reference
Сначала они также разрешают чистую ссылку на r-значение, кроме того, они определяют связка указателей вместо одного, что делает подход непригодным для использования в рамках класса:
class C
{
template <typename T>
void(*ptr)(T&& t); // fails (of course...)
};
Итак, теперь возникает вопрос: возможна ли вообще непрямая идеальная переадресация через указатели на функции?
Признаюсь, уже опасаясь ответа 'нет' (и в настоящее время возвращаясь к ссылкам на l-значения), но все еще надеясь, что где-то что-то упустил...
вы также не можете указать на перегрузку функций.
параметр шаблона должен быть выведен для идеальной работы переадресации
@NathanOliver Я знаю об этом, но я могу специализация, см. мой подход decltype
, только то, что я получил исключительно ссылку на r-значение. Хм... — тогда кажется, что ссылки универсальный вообще не существуют, а являются лишь гипотетической концепцией для объяснения — в то время как фактический тип ссылки разрешается дедукцией, как обычно, со ссылкой по умолчанию на r-значение. Если так (?), то понятно, почему я не могу (к сожалению...).
@appleapple Ну, на самом деле можно, но требуется приведение;) void f(int) { } void f(double) { } auto ptr = static_cast<void(*)(int)>(&f);
@Aconcagua, тогда вы указываете на одну функцию, а не на перегрузки.
@appleapple В моем случае это было бы совершенно нормально — при условии, что универсальные ссылки существует как отдельный тип. Однако, похоже, поскольку они на самом деле делают нет (просто используется для объяснения), поэтому мои попытки (очевидно) терпят неудачу...
«... существовал как отдельный тип ...» afaik универсальные ссылки не «существовали», пока Скотт не привлек внимание к этому конкретному использованию вывода типа аргумента функции T&&
. После этого им было дано новое имя («ссылка на пересылку»), чтобы обеспечить ясность/лучшие объяснения, но на самом деле это не какой-то новый тип ссылок.
@ 463035818_is_not_a_number Конечно, я вижу Теперь, это было не более чем отвлекающим маневром - и это не тот способ, которым я бы нуждался в их существовании;) Может быть, стоит предложить следующий стандарт или просто слишком экзотический вариант использования?
В первом примере используется вывод аргумента шаблона и правила свертывания ссылок для вычисления правильного типа. Когда вы явно указываете аргументы шаблона, это отключает вывод, и предоставленный тип заменяется напрямую, что приводит к ссылке rvalue.
Ну, и, очевидно, эти универсальные/пересылаемые ссылки (отвлекающий маневр...) являются тогда просто гипотетической концепцией для объяснения, но на самом деле они существуют, нет — как ни печально, поскольку это означает, что непрямая идеальная переадресация — это просто невозможно :(
ссылка на пересылку — это не только гипотетическая концепция. Это имя, данное определенному типу ссылки rvalue, которая имеет специальные правила вывода при выводе аргументов шаблона.
В частности, согласно [temp.deduct.call]/3:
A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
За исключением этого специального правила (и еще одного правила в [temp.deduct.type]), ссылка пересылки ведет себя точно так же, как и любая другая ссылка rvalue.
В частности, при предоставлении аргумента шаблона для T
не происходит вывода аргумента шаблона, и замена выполняется напрямую. При замене применяются обычные правила свертывания ссылок.
Таким образом, f<Test>
даст параметр функции Test&&
, который является ссылкой на rvalue, а f<Test&>
даст параметр функции Test&
(свернутый из &&
, примененный к Test&
).
Это также аргументы шаблона, которые будут выведены для аргумента rvalue и аргумента lvalue в вызове функции без явных аргументов шаблона. Если бы ссылка не была ссылкой для пересылки, то T
никогда нельзя было бы вывести в ссылку, а параметр функции после подстановки всегда был бы ссылкой на rvalue. Специальная корректировка A
, упомянутая в цитате, позволяет вывести T
к ссылочному типу lvalue, так что правила свертывания также приведут к параметру ссылочной функции lvalue.
Ссылки на пересылку могут существовать только в шаблонах. Функция или специализация шаблона не может иметь ссылку пересылки. Они не являются какой-либо другой категорией ссылок от ссылок rvalue/lvalue. Они работают в шаблонах только путем вывода к разным типам для разных категорий значений аргументов и создания различных специализаций для каждой категории значений.
Таким образом, поскольку указатель на функцию должен указывать на функция, а не на шаблон функции, то, какую из двух специализаций для категории значения аргумента выбрать, необходимо решить при получении указателя на функцию.
Если ожидается какой-либо вывод, то указатель на функцию не может этого предложить. Вместо этого следует использовать тип лямбда или функтор, который может выполнять дедукцию и может выбирать функцию или специализацию шаблона функции для вызова на основе категории значения.
Они гипотетические. Все, что существует, — это специальные правила вывода для — но они не существуют как еще один тип, параллельный ссылкам на l-значение и r-значение — такое впечатление производит статья Скота Мейера, но это просто отвлекающий маневр, как я узнал в тем временем...
Идеальная переадресация требует, чтобы аргумент шаблона был выведен. Указатель функции может указывать только на одну функцию, а не на набор перегрузок и не на шаблон функции.
Вы можете получить указатель функции на экземпляр ссылки на r-значение:
decltype(&f<Test&&>) ptr1 = &f<Test&&>;
ptr1(Test()); // output: move
или экземпляр l-значения:
decltype(&f<Test&>) ptr2 = &f<Test&>;
ptr2(t); // output: copy
Но не обоим одновременно. ptr1
и ptr2
— указатели на функции разного типа.
Если вы хотите, чтобы что-то содержало не одну функцию, а более одной перегрузки, вы можете использовать тип с функциями-членами. Например, с лямбда-выражением:
auto fw = [](auto&& t){ test(std::forward<decltype(t)>(t)); };
fw(t); // output: copy
fw(Test()); // output: move
Ну, с шаблонами все понятно. Отвлекающий маневр был статья Скотта Мейера, создающим впечатление, что такая вещь, как ссылка на пересылку, существует как тип — параллельно ссылкам на r-значение и ссылкам на l-значение. Таким образом, несуществование является основой для идеальной пересылки с ошибкой через указатели функций...
@Aconcagua, да, этот ответ не добавляет ничего нового. imho самая большая проблема, которая пошла не так с «переадресацией» / «универсальными» ссылками, заключается в том, что Скотт довольно успешно популяризировал термин «универсальная ссылка», но это не было принято в пользу «переадресации ссылок». Теперь наличие двух терминов для одного и того же добавляет путаницы. Кроме того, потому что статьи Скотта по этому вопросу по-прежнему называют их «универсальными» и не упоминают более «официальный» термин.
Ну, проблема не в названии. Мне все равно в данном контексте как они на самом деле называются. Я неверно истолковал статью, предполагая, что они существуют как отдельный тип в параллели для обычных ссылок на l- и r-значения. Ну, теперь я понял, совершенно неправильно... Таким образом, я не могу объявить указатель функции, используя его, поэтому через них не проходит идеальная пересылка...
Однако кажется, что это неверное толкование не совсем понятно из моего вопроса, поэтому все эти ответы о невозможности создавать указатели функций на шаблоны (о чем я хорошо знаю...).
Предыстория вопроса - неправильное прочтение статья о пересылке ссылок Скотта Мейера (там он называется универсальные ссылки).
В статье создалось впечатление, что такие ссылки пересылки существуют как отдельный тип в параллели для обычных ссылок l-value и r-value. Однако это не так, вместо этого это гипотетическая конструкция для объяснения специальных правил вывода, которые существуют, если параметры шаблона служат типами параметров функции.
Поскольку такого типа не существует, следовательно, невозможно объявить указатель функции с использованием этого типа, что доказывает невозможность реализации косвенной идеальной переадресации через указатель функции.
Указатель функции должен указывать на конкретную функцию. шаблоны функций не являются функциями, поэтому вы не можете указывать на них.