Variadic 템플릿 팩 확장
가변 템플릿과 기능을 배우려고합니다. 이 코드가 컴파일되지 않는 이유를 이해할 수 없습니다.
template<typename T>
static void bar(T t) {}
template<typename... Args>
static void foo2(Args... args)
{
(bar(args)...);
}
int main()
{
foo2(1, 2, 3, "3");
return 0;
}
컴파일 할 때 오류와 함께 실패합니다.
오류 C3520 : 'args':이 컨텍스트에서 매개 변수 팩을 확장해야합니다.
(함수에서 foo2
).
팩 확장이 발생할 수있는 곳 중 하나는 braced-init-list 내부 입니다. 더미 배열의 이니셜 라이저 목록 내부에 확장을 넣어이를 활용할 수 있습니다.
template<typename... Args>
static void foo2(Args &&... args)
{
int dummy[] = { 0, ( (void) bar(std::forward<Args>(args)), 0) ... };
}
이니셜 라이저의 내용을 더 자세히 설명하려면 :
{ 0, ( (void) bar(std::forward<Args>(args)), 0) ... };
| | | | |
| | | | --- pack expand the whole thing
| | | |
| | --perfect forwarding --- comma operator
| |
| -- cast to void to ensure that regardless of bar()'s return type
| the built-in comma operator is used rather than an overloaded one
|
---ensure that the array has at least one element so that we don't try to make an
illegal 0-length array when args is empty
데모 .
확장의 중요한 장점은 {}
왼쪽에서 오른쪽으로 평가할 수 있다는 것입니다.
C ++ 17 접기 표현식을 사용하면 다음과 같이 작성할 수 있습니다.
((void) bar(std::forward<Args>(args)), ...);
매개 변수 팩은 엄격하게 정의 된 컨텍스트 목록에서만 확장 할 수 있으며 연산자 ,
는 그중 하나가 아닙니다. 즉, pack 확장을 사용하여 operator로 구분 된 일련의 하위 표현식으로 구성된 표현식을 생성 할 수 없습니다 ,
.
경험의 규칙은 "확장은 목록 구분 기호 가있는 -분리 된 패턴 목록 을 생성 할 수 있습니다 ."입니다. 연산자 는 문법적으로 목록을 구성하지 않습니다.,
,
,
각 인수에 대한 함수를 호출하려면 재귀 (가변 템플릿 프로그래머 상자의 기본 도구)를 사용할 수 있습니다.
template <typename T>
void bar(T t) {}
void foo2() {}
template <typename Car, typename... Cdr>
void foo2(Car car, Cdr... cdr)
{
bar(car);
foo2(cdr...);
}
int main()
{
foo2 (1, 2, 3, "3");
}
매개 변수 팩은 엄격하게 정의 된 컨텍스트 목록에서만 확장 할 수 있으며 연산자 ,
는 그중 하나가 아닙니다. 즉, pack 확장을 사용하여 operator로 구분 된 일련의 하위 표현식으로 구성된 표현식을 생성 할 수 없습니다 ,
.
경험의 규칙은 "확장은 목록 구분 기호가있는 ,
-분리 된 패턴 목록을 생성 할 수 있습니다 ,
."입니다. 연산자 ,
는 문법적으로 목록을 구성하지 않습니다.
각 인수에 대한 함수를 호출하려면 재귀 (가변 템플릿 프로그래머 상자의 기본 도구)를 사용할 수 있습니다.
#include <utility>
template<typename T>
void foo(T &&t){}
template<typename Arg0, typename Arg1, typename ... Args>
void foo(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){
foo(std::forward<Arg0>(arg0));
foo(std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
auto main() -> int{
foo(1, 2, 3, "3");
}
복사되지 않은 유용한 정보
이 답변에서 보지 못한 또 다른 것은 &&
지정자 및 std::forward
. C ++에서 &&
지정자는 rvalue-references 또는 범용 참조 중 하나를 의미 할 수 있습니다.
나는 rvalue-references에 들어 가지 않고 가변 템플릿으로 작업하는 누군가에게 설명 할 것입니다. 보편적 인 참조는 신이 보낸 것입니다.
완벽한 전달
std::forward
범용 참조 의 용도 중 하나는 유형을 다른 함수로 완벽하게 전달하는 것입니다.
In your example, if we pass an int&
to foo2
it will be automatically demoted to int
because of the signature of the generated foo2
function after template deduction and if you wanted to then forward this arg
to another function that would modify it by reference, you will get undesired results (the variable won't be changed) because foo2
will be passing a reference to the temporary created by passing an int
to it. To get around this, we specify a forwarding function to take any type of reference to a variable (rvalue or lvalue). Then, to be sure that we pass the exact type passed in the forwarding function we use std::forward
, then and only then do we allow the demoting of types; because we are now at the point where it matters most.
If you need to, read more on universal references and perfect forwarding; Scott Meyers is pretty great as a resource.
You can use make_tuple
for pack expansion as it introduces a context where the ,
sequence produced by an expansion is valid
make_tuple( (bar(std::forward<Args>(args)), 0)... );
Now, I suspect the unused/unnamed/temporary tuple of zeroes that's produced is detectable by the compiler and optimized away.
The C++17 solution to this is really close to your expected code:
template<typename T>
static void bar(T t) {}
template<typename... Args>
static void foo2(Args... args) {
(bar(args), ...);
}
int main() {
foo2(1, 2, 3, "3");
return 0;
}
This expand the pattern with the comma operator between every expression
// imaginary expanded expression
(bar(1), bar(2), bar(3), bar("3"));
This is a full example, based on the answers here.
Example to reproduce console.log
as seen in JavaScript:
Console console;
console.log("bunch", "of", "arguments");
console.warn("or some numbers:", 1, 2, 3);
console.error("just a prank", "bro");
Filename e.g. js_console.h
:
#include <iostream>
#include <utility>
class Console {
protected:
template <typename T>
void log_argument(T t) {
std::cout << t << " ";
}
public:
template <typename... Args>
void log(Args&&... args) {
int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
cout << endl;
}
template <typename... Args>
void warn(Args&&... args) {
cout << "WARNING: ";
int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
cout << endl;
}
template <typename... Args>
void error(Args&&... args) {
cout << "ERROR: ";
int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
cout << endl;
}
};
참고URL : https://stackoverflow.com/questions/25680461/variadic-template-pack-expansion
'programing tip' 카테고리의 다른 글
initWithRootViewController 이외의 메소드로 UINavigationController의 rootViewController 설정 (0) | 2020.11.07 |
---|---|
모든 bower 패키지를 어떻게 제거합니까? (0) | 2020.11.07 |
Spyder 편집기 배경을 어둡게 변경하는 방법은 무엇입니까? (0) | 2020.11.07 |
ASP.NET Development Server 대신 IIS에 디버거를 연결하려면 어떻게합니까? (0) | 2020.11.07 |
Pages # home의 Rails ExecJS :: ProgramError? (0) | 2020.11.06 |