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

#c #c 20 #rvalue-reference #perfect-forwarding #forwarding-reference

#c #c 20 #rvalue-ссылка #идеальная пересылка #пересылка-ссылка

Вопрос:

Рассмотрим функцию, подобную этой:

 autoamp;amp; just(autoamp;amp; arg) { return std::forward<decltype(arg)>(arg); }
  

Это улучшит пересылку всех ссылок и вернет мертвую ссылку при вызове для временного объекта.

Подробнее:

 struct Something {};
auto something() { return Something{}; }

autoamp;amp; ref = just(something()); // ERROR: Here ref refers to already destructed Something.

autoamp;amp; hold = something();
autoamp;amp; ref = just(hold);   // OK: Here ref refers to lifetime-extended Something.
  

Итак, вопрос: как можно определить just , чтобы первый вызов не смог скомпилироваться, а второй вызов будет скомпилирован?

Комментарии:

1. Требуется ссылка на значение lvalue ?

2. just(autoamp;amp; arg) гм, какую версию C вы используете? Эта функция не должна компилироваться в c 11, 14 или 17 без -fconcepts .

3. @Elliott Да, gcc-9.3 с -std=c 17 -fconcepts

4. isocpp.github.io/CppCoreGuidelines/…

5. @Vahagn, нет проблем. Кроме того, концептуальный ответ не сработал бы, если тип аргумента шаблона равен rvalue . Так just<Somethingamp;amp;>(std::move(hold)) будет компилироваться, что, я думаю, было не тем, что хотел сделать AndyG. Это можно было бы легко исправить, изменив его на что-то вроде requires std::is_lvalue_reference_v<T> или более интуитивно is_lvalue_reference_v<Tamp;amp;> .

Ответ №1:

Как πάντα ῥεῖ указано в комментариях, если вам требуется значение lvalue, вы должны написать свою функцию таким образом (single amp; ):

[из вашего комментария я использовал стиль c 17]

 template <typename Arg>
auto amp;amp; just(Arg amp; arg);
  

Однако я буду считать, что ваша реальная проблема не так проста и что вы предпочитаете кодировать требования внутри функции:

 #include <type_traits>

template <typename Arg>
auto amp;amp; just(Arg amp;amp; arg) {

    static_assert(std::is_lvalue_reference_v<Argamp;amp;>);

    return std::forward<Arg>(arg);
}
  

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

 #include <iostream>
#include <type_traits>

template <typename Func>
auto amp;amp; just (Func amp;amp; func)
{
    using return_type = decltype(func());
    
    static_assert(std::is_reference_v<return_type>, "returning temportary");

    // calculate arguments for func
    // (no args here for simplicity)

    return func(); // always return ref
}

int global;
int ReturnCopy () { return int(); }
int amp; ReturnGlobal () { return global; }
int amp;amp; ReturnTemporary () { return std::move(global); }

int main ()
{
    just(ReturnCopy); // <--- error
    just(ReturnGlobal);
    just(ReturnTemporary);
}