나는 당신이 직면 한 유스 케이스를 이해하는데 약간의 오해를 느낍니다.
모든
먼저,이 함수 템플릿입니다 :
struct A
{
template <typename... Args>
void f(Args... args)
{
}
};
그리고이 함수 템플릿되지 않습니다 : (함수 템플릿) 인수의 형태 공제 이전의 정의에서
template <typename... Args>
struct A
{
void f(Args... args)
{
}
};
일어난다. 후자에는 유형 공제가 없습니다.
함수 템플릿을 사용하고 있지 않습니다. 클래스 템플릿에서 템플릿이 아닌 멤버 함수를 사용하고 있으며이 특정 멤버 함수의 경우 서명이 고정되어 있습니다.
template <typename T, T t>
struct trap;
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(Args... args);
};
와 그 멤버 함수를 참조하여 다음과 같은 :
&trap<decltype(&Base::target), &Base::target>::call;
정적 템플릿이 아닌 call
함수에 대한 포인터와 함께 결국 아래처럼 trap
클래스를 정의함으로써
고정 된 서명으로 target
함수의 서명과 동일합니다.
이제 call
함수는 중간 호출자 역할을합니다.
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(Args... args)
{
return (get_base()->*t)(args...);
}
};
는 trap
클래스 템플릿이 인스턴스화하는 데 사용되는 target
기능을 가정 : 대답, 당신은 call
함수를 호출되며, 그 기능은 target
의 매개 변수를 초기화하는 자신의 인수를 전달 target
멤버 함수를 호출합니다 정의 다음과 같이
struct Base
{
int target(Noisy& a, Noisy b);
};
을 다음 call
기능을 끝낸다 trap
클래스를 인스턴스화하여 :
,536,913,632 10
// what the compiler *sees*
static int call(Noisy& a, Noisy b)
{
return get_base()->target(a, b);
}
다행히도 a
는 참조의해 전달되며, 이는 단지 target
의 파라미터 참조 동종 의해 전달되고 결합된다.
- 다음
Noisy
클래스가 이동하는 경우에 상관없이 또는 하나의 값에 의해 을 통과하기 때문에, 당신은 b
인스턴스의 복사본을 여러 개 만들고있어하지 - 불행히도, 이것은 b
개체에 대한 보유하지 않습니다 첫 번째 경우 : call
함수가 외부 컨텍스트에서 자체 호출 될 때.
call
의 본문에서 target
함수를 호출 할 때 b
인스턴스를 복사 할 때.
DEMO 1
이
다소 비효율적이다 : 단지 당신이 가 xValue에 b
인스턴스를 켤 수 있다면 당신은 이동 - 생성자 호출로 돌려, 적어도 하나의 복사 생성자 호출을 저장 할 수 : 이제는 두 번째 매개 변수 대신 이동 생성자를 호출합니다.
지금까지는 그렇게 좋았지 만 수동으로 수행했습니다 (std::move
은 이동 의미를 적용하는 것이 안전하다는 것을 알고 추가했습니다).? 이제 문제는 매개 변수 팩에 작동 할 때 동일한 기능을 적용 할 수있는 방법입니다 :
return get_base()->target(std::move(args)...); // WRONG!
당신은 각각의 매개 변수 팩 내의 모든 인수에 std::move
전화를 적용 할 수 없습니다. 모든 인수에 똑같이 적용되면 컴파일러 오류가 발생할 수 있습니다.
DEMO 2
다행히도, 비록 Args...
는 std::forward
도우미 함수가 대신 사용될 수있다하는 전달 참조 아니다. 즉 <T>
타입 std::forward<T>
에 무슨 (좌변 참조 또는 비 좌변 참조)를 std::forward
다르게 행동 할 따라이다 좌변 참조 용
(예 T
는 Noisy&
경우) 값 표현의 카테고리는 lvalue (즉, Noisy&
)로 남습니다. 식의 값이 카테고리가 xValue된다 (즉 Noisy&&
) (T
는 Noisy&&
또는 일반 Noisy
경우 예) 비 - 참조의 좌변
. 가 xValue에 b
관련된 식의 값의 카테고리를 선회
static int call(Noisy& a, Noisy b)
{
// what the compiler *sees*
return get_base()->target(std::forward<Noisy&>(a), std::forward<Noisy>(b));
}
: 만약 끝낼
static R call(Args... args)
{
return (get_base()->*t)(std::forward<Args>(args)...);
}
:
아래와 같이 target
함수를 정의하여, 즉 상기 데 b
이고, 이는 Noisy&&
입니다. 이를 통해 컴파일러는 a
을 그대로두고 target
함수의 두 번째 매개 변수를 초기화하는 이동 생성자를 선택할 수 있습니다.
DEMO 3(DEMO 1 출력과 비교)
기본적으로, 이것은 std::forward
가되는 것이다. 일반적으로 std::forward
은 전달 참조과 함께 사용되며 여기에서 T
은 전달 참조에 대한 유형 공제 규칙에 따라 추론 된 유형을 보유합니다. 해당 형식 (인수의 값 범주에 의존하지 않음)에 따라 다른 동작을 적용하므로 항상 <T>
부분을 명시 적으로 전달해야합니다. 명시 적 형식 템플릿 인수 <T>
이 없으면 std::forward
은 (매개 변수 팩을 확장 할 때처럼) 이름을 통해 참조되는 인수에 대한 좌변 값 참조를 항상 추론합니다.
추가적으로은 다른 유형을 전달하는 동안 한 유형에서 다른 유형으로 일부 인수를 변환하려고합니다.당신이 매개 변수 팩에서 std::forward
ING 인수와 트릭에 대해 걱정하지 않는다, 그것은 항상 복사 생성자를 호출 괜찮 경우, 다음 버전 확인이다 : 그러나
template <typename T> // transparent function
T&& process(T&& t) {
return std::forward<T>(t);
}
Bar process(Foo x) { // overload for specific type of arguments
return Bar{x};
}
//...
get_base()->target(process(args)...);
DEMO 4
당신이 데모에서 그 Noisy
인수의 사본을 피하려는 경우, 당신은 std::forward
적절한 동작을 적용 할 수 있도록 어떻게 든 (즉, process
전화, 과 Args
종류 이상 패스 t을 std::forward
전화를 결합해야 xvalues로 보내거나 아무것도하지 않음). 방금 구현할 수있는 방법에 대한 간단한 예를 들었습니다.
template <typename T, typename U>
T&& process(U&& u) {
return std::forward<T>(std::forward<U>(u));
}
template <typename T>
Bar process(Foo x) {
return Bar{x};
}
//...
get_base()->target(process<Args>(args)...);
그러나 이는 방금 옵션 중 하나 일뿐입니다. 당신이 process
기능 (버전)을 호출하기 전에 std::forward
있도록 호출 할 때, 간단하게 다시 작성하거나 다시 정렬 할 수 있습니다
get_base()->target(process(std::forward<Args>(args))...);
DEMO 5이
그리고 (DEMO 4의 출력을 비교) 잘 작동합니다 (즉, 버전과 함께). 따라서 요점은 추가 코드 std::forward
은 코드를 조금 최적화하는 것이며, provided idea은 해당 기능을 구현할 수있는 한 가지 예일뿐입니다 (알 수 있듯이 동일한 효과가 나타납니다).
이러한 예제에는 프로세스 함수가 호출되는 컨텍스트가 없습니다. 항상 명시 적 형식 템플릿 인수가 있습니다 (적어도 비 lvalue-reference를 xvalues로 바꾸는 아이디어였습니다). 당신은 당신의 버전으로 도망 갈 수 있지만 대상 함수의 매개 변수를 초기화하는 동안 항상 copy ctor를 호출합니다. –
버전 1이 어떻게 든 "더티"라는 것에 동의합니다. 왜냐하면 템플릿이 아닌 함수의 과부하가 템플릿과 다르기 때문입니다 반환 유형별. 그러나 버전 2에서는 어떤 의미도 볼 수 없습니다. 컴파일러가 사용 컨텍스트에서 그것을 추론 할 수 없으므로 항상 첫 번째 템플릿 매개 변수를 지정해야합니다. –
@PiotrS., 문맥을 제공하기 위해 편집했습니다 (내 실제 사용 사례와 일치한다고 생각합니다). 지난 며칠 동안 귀중한 도움을 주신 덕분에, 저는 이제 작업 솔루션 ([here] (http://stackoverflow.com/q/27866483/435129))을 얻었습니다. 이것은 내가 이해하지 못하는 코드를 통합하는 것이 싫어서 내가 씹고있는 마지막 구성 요소입니다. –