C ++ 0x는 다음을 사용하는 예를 보여줍니다 std::forward
.
template<class T>
void foo(T&& arg)
{
bar(std::forward<T>(arg));
}
언제 사용하는 것이 유리 std::forward
합니까?
또한 &&
매개 변수 선언 에 사용해야 합니다. 모든 경우에 유효합니까? 함수가 선언 된 경우 임시 함수를 함수에 전달해야한다고 생각 &&
했으므로 매개 변수로 foo를 호출 할 수 있습니까?
마지막으로 다음과 같은 함수 호출이있는 경우 :
template<int val, typename... Params>
void doSomething(Params... args) {
doSomethingElse<val, Params...>(args...);
}
대신 이것을 사용해야합니까?
template<int val, typename... Params>
void doSomething(Params&&... args) {
doSomethingElse<val, Params...>(std::forward<Params>(args)...);
}
또한 함수에서 매개 변수를 두 번 사용하는 경우 (즉, 동시에 두 개의 함수로 전달) 사용하는 것이 std::forward
좋습니까? std::forward
같은 것을 일시적으로 두 번 변환 하지 않고 메모리를 이동하고 두 번째 사용에는 유효하지 않습니까? 다음 코드가 정상입니까?
template<int val, typename... Params>
void doSomething(Params&&... args) {
doSomethingElse<val, Params...>(std::forward<Params>(args)...);
doSomethingWeird<val, Params...>(std::forward<Params>(args)...);
}
나는 약간 혼란스럽고 std::forward
기꺼이 정리를 사용합니다.
답변
첫 번째 예제처럼 사용하십시오.
template <typename T> void f(T && x)
{
g(std::forward<T>(x));
}
template <typename ...Args> void f(Args && ...args)
{
g(std::forward<Args>(args)...);
}
그 때문에의의 규칙을 무너 참조 : 만약 T = U&
, 다음 T&& = U&
,하지만 만약 T = U&&
다음 T&& = U&&
, 당신은 항상 함수 본문 내부의 올바른 유형으로 끝낼 수 있도록. 마지막으로 forward
lvalue-turned x
(지금 이름이 있기 때문에)를 rvalue 참조로 되돌려 야합니다.
즉 일반적으로 의미하지 않기 때문에 당신은, 그러나 한 번 이상 전달하지 뭔가를해야한다 : 당신은 가능성이 있다는 것을 전달 수단 움직이는 모든 방법을 최종 호출을 통해 인수를하고 이동 된 후에는 당신이 그것을 사용하지 수 있도록이 사라 졌어요 다시 (당신이 아마 의도했던 방식으로).
답변
Kerrek의 답변은 매우 유용하지만 제목의 질문에 완전히 대답하지는 않습니다.
언제 std :: forward를 사용하여 인수를 전달합니까?
이에 대한 답을 얻기 위해서는 먼저 보편적 참조 개념을 도입해야합니다 . Scott Meyers는이 이름을 지정했으며 오늘날에는 종종 전달 참조라고합니다. 기본적으로 다음과 같은 것을 볼 때 :
template<typename T>
void f(T&& param);
param
rvalue 참조가 아니라 결론을 내릴 수도 있음 을 명심하십시오 . 범용 참조는 매우 제한된 형식 ( T&&
const 또는 유사한 한정자가없는)과 유형 추론에 의해 특징 지어집니다 . 유형 이 호출 T
될 때 유형 이 추론 f
됩니다. 간단히 말해 범용 참조는 rvalue로 초기화 된 경우 rvalue 참조에 해당하고 lvalue로 초기화 된 경우 lvalue 참조에 해당합니다.
이제 원래 질문에 대답하기가 비교적 쉽습니다 std::forward
.
- 함수에서 마지막으로 사용 된 범용 참조
- 값으로 반환되는 함수에서 반환되는 범용 참조
첫 번째 사례의 예 :
template<typename T>
void foo(T&& prop) {
other.set(prop); // use prop, but don't modify it because we still need it
bar(std::forward<T>(prop)); // final use -> std::forward
}
위의 코드에서 완료 prop
후 알 수없는 값을 원하지 other.set(..)
않기 때문에 전달이 발생하지 않습니다. 그러나, 우리가 끝났을 때 bar
앞으로 전화 를 걸면 원하는대로 무엇이든 할 수 있습니다 (예 : 이동).prop
bar
두 번째 경우의 예 :
template<typename T>
Widget transform(T&& prop) {
prop.transform();
return std::forward<T>(prop);
}
이 함수 템플릿은 prop
rvalue 인 경우 반환 값으로 이동 하고 lvalue 인 경우 복사해야합니다. std::forward
마지막에 생략 한 경우에는 항상 사본을 작성하는데, 이는 prop
rvalue 일 때 더 비쌉니다 .
* 완전히 참조하기 위해 범용 참조는 cv-unqualified 템플릿 매개 변수에 대한 rvalue 참조를 취하는 개념입니다.
답변
이 예가 도움이됩니까? 나는 std :: forward의 유용한 비 일반적인 예를 찾기 위해 고심했지만, 우리가 인수로 입금하기 위해 현금을 통과하는 은행 계좌의 예에 부딪쳤다.
만약 우리가 계정의 const 버전을 가지고 있다면 우리가 예금 템플릿 <>에 전달할 때 const 함수가 호출 될 것으로 예상해야합니다. 그런 다음 예외가 발생합니다 (이 아이디어는 잠긴 계정이었습니다!)
const 계정이 아닌 계정이 있으면 계정을 수정할 수 있어야합니다.
#include <iostream>
#include <string>
#include <sstream> // std::stringstream
#include <algorithm> // std::move
#include <utility>
#include <iostream>
#include <functional>
template<class T> class BankAccount {
private:
const T no_cash {};
T cash {};
public:
BankAccount<T> () {
std::cout << "default constructor " << to_string() << std::endl;
}
BankAccount<T> (T cash) : cash (cash) {
std::cout << "new cash " << to_string() << std::endl;
}
BankAccount<T> (const BankAccount& o) {
std::cout << "copy cash constructor called for " << o.to_string() << std::endl;
cash = o.cash;
std::cout << "copy cash constructor result is " << to_string() << std::endl;
}
// Transfer of funds?
BankAccount<T> (BankAccount<T>&& o) {
std::cout << "move cash called for " << o.to_string() << std::endl;
cash = o.cash;
o.cash = no_cash;
std::cout << "move cash result is " << to_string() << std::endl;
}
~BankAccount<T> () {
std::cout << "delete account " << to_string() << std::endl;
}
void deposit (const T& deposit) {
cash += deposit;
std::cout << "deposit cash called " << to_string() << std::endl;
}
friend int deposit (int cash, const BankAccount<int> &&account) {
throw std::string("tried to write to a locked (const) account");
}
friend int deposit (int cash, const BankAccount<int> &account) {
throw std::string("tried to write to a locked (const) account");
}
friend int deposit (int cash, BankAccount<int> &account) {
account.deposit(cash);
return account.cash;
}
friend std::ostream& operator<<(std::ostream &os, const BankAccount<T>& o) {
os << "$" << std::to_string(o.cash);
return os;
}
std::string to_string (void) const {
auto address = static_cast<const void*>(this);
std::stringstream ss;
ss << address;
return "BankAccount(" + ss.str() + ", cash $" + std::to_string(cash) + ")";
}
};
template<typename T, typename Account>
int process_deposit(T cash, Account&& b) {
return deposit(cash, std::forward<Account>(b));
}
int main(int, char**)
{
try {
// create account1 and try to deposit into it
auto account1 = BankAccount<int>(0);
process_deposit<int>(100, account1);
std::cout << account1.to_string() << std::endl;
std::cout << "SUCCESS: account1 deposit succeeded!" << std::endl;
} catch (const std::string &e) {
std::cerr << "FAILED: account1 deposit failed!: " << e << std::endl;
}
try {
// create locked account2 and try to deposit into it; this should fail
const auto account2 = BankAccount<int>(0);
process_deposit<int>(100, account2);
std::cout << account2.to_string() << std::endl;
std::cout << "SUCCESS: account2 deposit succeeded!" << std::endl;
} catch (const std::string &e) {
std::cerr << "FAILED: account2 deposit failed!: " << e << std::endl;
}
try {
// create locked account3 and try to deposit into it; this should fail
auto account3 = BankAccount<int>(0);
process_deposit<int>(100, std::move(account3));
std::cout << account3.to_string() << std::endl;
std::cout << "SUCCESS: account3 deposit succeeded!" << std::endl;
} catch (const std::string &e) {
std::cerr << "FAILED: account3 deposit failed!: " << e << std::endl;
}
}
짓다:
cd std_forward
rm -f *.o example
c++ -std=c++2a -Werror -g -ggdb3 -Wall -c -o main.o main.cpp
c++ main.o -o example
./example
예상 출력 :
# create account1 and try to deposit into it
new cash BankAccount(0x7ffee68d96b0, cash $0)
deposit cash called BankAccount(0x7ffee68d96b0, cash $100)
BankAccount(0x7ffee68d96b0, cash $100)
# SUCCESS: account1 deposit succeeded!
delete account BankAccount(0x7ffee68d96b0, cash $100)
# create locked account2 and try to deposit into it; this should fail
new cash BankAccount(0x7ffee68d9670, cash $0)
delete account BankAccount(0x7ffee68d9670, cash $0)
# FAILED: account2 deposit failed!: tried to write to a locked (const) account
# create locked account3 and try to deposit into it; this should fail
new cash BankAccount(0x7ffee68d9630, cash $0)
delete account BankAccount(0x7ffee68d9630, cash $0)
# FAILED: account3 deposit failed!: tried to write to a locked (const) account