perfect forwarding
전달받은 인자를 다른 함수에게 값, const/volatile 속성, value category 등의 변화없이 그대로 전달
perfect forwarding을 하려면
#include <iostream>
void hoo(int&& r) {}
// 함수가 수행되는데 걸린 시간을 구하는 함수 템플릿
// 전달받은 인자를 원래 함수에 전달해야함
template<class F>
void chronometry(F f, int& arg)
{
f(arg);
}
template<class F>
void chronometry(F f, int&& arg)
{
// f(arg); // arg라는 이름이 있으므로 lvalue가 되어버림
f(static_cast<int&&>(arg)); // rvalue로 다시 캐스팅
}
int main()
{
hoo(10); // ok
chronometry(hoo, 10);
}
int&, int&&을 자동 생성할 수 있는데
템플릿을 사용하려면 구현이 동일해야함
void goo(int& n) { n = 20;}
void hoo(int&& r) {}
template<class F, class T>
void chronometry(F f, T&& arg)
{
// f(static_cast<T&&>(arg));
f(std::forward<T>(arg));
}
int main()
{
int n = 0;
chronometry(goo, n);
chronometry(hoo, 10);
std::cout << n << std::endl; // 20
}
lvalue를 (함수로 전달하면) lvalue로 캐스팅하고
rvalue를 (함수로 전달하면 lvalue로 변경되었던 걸 다시) rvalue로 캐스팅
인자가 rvalue인 경우만 std::move()를 하는 것!
void foo() {}
int& goo(int a, int& b, int&& c)
{
b = 20;
return b;
}
template<class F, class ... T>
decltype(auto) chronometry(F f, T&& ... arg)
{
return f(std::forward<T>(arg)...);
}
int main()
{
int n = 0;
int& ret = chronometry(goo, 10, n, 10);
chronometry(foo);
ret = 100;
std::cout << n << std::endl; // 100
}
std::invoke()를 사용하여 멤버 함수 포인터도 동작되도록 수정
#include <functional>
void foo(int n) {}
class Test
{
public:
void f1(int n) { std::cout << "Test f1" << std::endl; }
};
template<class F, class ... T>
decltype(auto) chronometry(F f, T&& ... arg)
{
// return f(std::forward<T>(arg)...);
return std::invoke(f, std::forward<T>(arg)...);
}
int main()
{
chronometry(foo, 10);
Test obj;
chronometry(&Test::f1, &obj, 10);
}
#include <functional>
struct Functor
{
void operator()(int n) &
{
std::cout << "operator() &" << std::endl;
}
void operator()(int n) &&
{
std::cout << "operator() &&" << std::endl;
}
};
template<class F, class ... T>
decltype(auto) chronometry(F&& f, T&& ... arg)
{
return std::invoke(std::forward<F>(f),
std::forward<T>(arg)...);
}
int main()
{
Functor f;
f(10); // f.operator()(10) // &
Functor()(10); // &&
chronometry(f, 10); // &
chronometry(Functor(), 10); // &&
}
#include <functional>
template<class F, class ... T>
decltype(auto) chronometry(F&& f, T&& ... arg)
{
return std::invoke(std::forward<F>(f), std::forward<T>(arg)...);
}
void foo(int* p) { }
int main()
{
foo(0); // ok, 0은 포인터로 암시적 형변환 가능
// int arg = 0;
// foo(arg); // error, int 변수는 포인터로 암시적 형변환 불가능
// int&& arg = 0로 들어가 암시적 형변환 불가능
// chronometry(foo, 0); // error
// std::nullptr_t arg = nullptr로 들어감
// nullptr은 모든 종류의 포인터로 암시적 형변환 가능
chronometry(foo, nullptr); // ok
}
#include <functional>
template<class F, class ... T>
decltype(auto) chronometry(F&& f, T&& ... arg)
{
return std::invoke(std::forward<F>(f), std::forward<T>(arg)...);
}
void foo(std::pair<int, int> p) {}
void goo(int a) {}
void goo(int a, int b) {}
int main()
{
// foo({1,2}); // ok
// chronometry(foo, {1,2}); // error, 템플릿으로 받아 전달할 수 없음 (foo()에 바로 보낼 땐 정확한 타입이 존재)
chronometry(foo, std::pair{1,2}); // ok
goo(1);
goo(1,2);
// chronometry(goo, 1, 2); // error, 어느 함수인지 판단할 수 없는 ambiguous 문제 (함수의 주소를 보내기 전 어느 함수인지 결정되어야 함)
chronometry(static_cast<void(*)(int, int)>(goo), 1, 2); // ok
}