模板化 C++ 中函数的完美转发 class
Perfect forwarding for functions inside of a templated C++ class
有没有什么好的方法可以完美转发模板化 class 中的函数?具体来说,在代码
#include <iostream>
// Forward declare a Bar
struct Bar;
// Two different functions that vary based on the kind of argument
void printme(Bar const & bar) {
std::cout << "printme: constant reference bar" << std::endl;
}
void printme(Bar && bar) {
std::cout << "printme: r-value reference bar" << std::endl;
}
void printme2(Bar const & bar) {
std::cout << "printme2: constant reference bar" << std::endl;
}
void printme2(Bar && bar) {
std::cout << "printme2: r-value reference bar" << std::endl;
}
// Some class with a bunch of functions and possible some data (though, not
// in this one)
template <typename T>
struct Foo {
void baz(T && t) {
printme(std::forward <T> (t));
}
void buz(T && t) {
printme2(std::forward <T> (t));
}
};
struct Bar {};
int main() {
Foo <Bar> foo;
foo.baz(Bar());
// Causes an error
Bar bar;
//foo.buz(bar);
}
取消注释最后一段代码,我们得到错误:
test03.cpp: In function 'int main()':
test03.cpp:51:16: error: cannot bind 'Bar' lvalue to 'Bar&&'
foo.buz(bar);
^
test03.cpp:30:10: note: initializing argument 1 of 'void Foo<T>::buz(T&&) [with T = Bar]'
void buz(T && t) {
^
Makefile:2: recipe for target 'all' failed
make: *** [all] Error 1
现在,我们可以通过将模板参数移到 class:
中来解决这个问题
#include <iostream>
// Forward declare a Bar
struct Bar;
// Two different functions that vary based on the kind of argument
void printme(Bar const & bar) {
std::cout << "printme: constant reference bar" << std::endl;
}
void printme(Bar && bar) {
std::cout << "printme: r-value reference bar" << std::endl;
}
void printme2(Bar const & bar) {
std::cout << "printme2: constant reference bar" << std::endl;
}
void printme2(Bar && bar) {
std::cout << "printme2: r-value reference bar" << std::endl;
}
// Some class with a bunch of functions and possible some data (though, not
// in this one)
template <typename T>
struct Foo {
void baz(T && t) {
printme(std::forward <T> (t));
}
template <typename T_>
void buz(T_ && t) {
printme2(std::forward <T_> (t));
}
};
struct Bar {
Bar() {}
};
int main() {
Foo <Bar> foo;
foo.baz(Bar());
Bar bar;
foo.buz(bar);
}
但是,这似乎真的很冗长。例如,假设我们有大量函数都依赖于 T
类型并且需要完美转发。我们需要为每个单独的模板声明。此外,class Foo
可能包含 T
类型的数据,我们需要与此数据一致的函数。当然,类型检查器会发现不匹配,但这个系统并不像只有一个模板参数 T
.
那样简单
基本上,我想知道是否有更好的方法来做到这一点,或者我们只是坚持分别为 class 中的每个函数制作模板?
模板非常不同。
在第一个代码中,你的模板参数是 Bar
,所以我什至不确定它在做什么,因为你不应该使用 std::forward
除了引用限定类型.我 认为 它是 void buz(Bar&& t) {printme((Bar)t);}
,并且编译器不愿将 main
中的左值 bar
传递给期望 Bar&&
的函数。
在第二个代码块中,由于通用引用,模板参数为Bar&
,因此代码为void buz(Bar& t) {printme((Bar&)t);}
,绑定到[=16]中的左值bar
=]就好了。
如果你想要完美的转发,模板参数必须是你想要传递的右值限定类型,这意味着你几乎总是必须让函数本身被模板化。但是,帮自己一个忙,给它取个不同的名字。 T
和 T_
将是 不同类型 。
template <typename U>
void buz(U&& t) {
printme2(std::forward<U>(t));
}
如果你想要SFINAE,你也可以添加:
template <typename U,
typename allowed=typename std::enable_if<std::is_constructible<Bar,U>::value,void*>::type
>
void buz(U && t) {
printme2(std::forward <U> (t));
}
有没有什么好的方法可以完美转发模板化 class 中的函数?具体来说,在代码
#include <iostream>
// Forward declare a Bar
struct Bar;
// Two different functions that vary based on the kind of argument
void printme(Bar const & bar) {
std::cout << "printme: constant reference bar" << std::endl;
}
void printme(Bar && bar) {
std::cout << "printme: r-value reference bar" << std::endl;
}
void printme2(Bar const & bar) {
std::cout << "printme2: constant reference bar" << std::endl;
}
void printme2(Bar && bar) {
std::cout << "printme2: r-value reference bar" << std::endl;
}
// Some class with a bunch of functions and possible some data (though, not
// in this one)
template <typename T>
struct Foo {
void baz(T && t) {
printme(std::forward <T> (t));
}
void buz(T && t) {
printme2(std::forward <T> (t));
}
};
struct Bar {};
int main() {
Foo <Bar> foo;
foo.baz(Bar());
// Causes an error
Bar bar;
//foo.buz(bar);
}
取消注释最后一段代码,我们得到错误:
test03.cpp: In function 'int main()':
test03.cpp:51:16: error: cannot bind 'Bar' lvalue to 'Bar&&'
foo.buz(bar);
^
test03.cpp:30:10: note: initializing argument 1 of 'void Foo<T>::buz(T&&) [with T = Bar]'
void buz(T && t) {
^
Makefile:2: recipe for target 'all' failed
make: *** [all] Error 1
现在,我们可以通过将模板参数移到 class:
中来解决这个问题#include <iostream>
// Forward declare a Bar
struct Bar;
// Two different functions that vary based on the kind of argument
void printme(Bar const & bar) {
std::cout << "printme: constant reference bar" << std::endl;
}
void printme(Bar && bar) {
std::cout << "printme: r-value reference bar" << std::endl;
}
void printme2(Bar const & bar) {
std::cout << "printme2: constant reference bar" << std::endl;
}
void printme2(Bar && bar) {
std::cout << "printme2: r-value reference bar" << std::endl;
}
// Some class with a bunch of functions and possible some data (though, not
// in this one)
template <typename T>
struct Foo {
void baz(T && t) {
printme(std::forward <T> (t));
}
template <typename T_>
void buz(T_ && t) {
printme2(std::forward <T_> (t));
}
};
struct Bar {
Bar() {}
};
int main() {
Foo <Bar> foo;
foo.baz(Bar());
Bar bar;
foo.buz(bar);
}
但是,这似乎真的很冗长。例如,假设我们有大量函数都依赖于 T
类型并且需要完美转发。我们需要为每个单独的模板声明。此外,class Foo
可能包含 T
类型的数据,我们需要与此数据一致的函数。当然,类型检查器会发现不匹配,但这个系统并不像只有一个模板参数 T
.
基本上,我想知道是否有更好的方法来做到这一点,或者我们只是坚持分别为 class 中的每个函数制作模板?
模板非常不同。
在第一个代码中,你的模板参数是 Bar
,所以我什至不确定它在做什么,因为你不应该使用 std::forward
除了引用限定类型.我 认为 它是 void buz(Bar&& t) {printme((Bar)t);}
,并且编译器不愿将 main
中的左值 bar
传递给期望 Bar&&
的函数。
在第二个代码块中,由于通用引用,模板参数为Bar&
,因此代码为void buz(Bar& t) {printme((Bar&)t);}
,绑定到[=16]中的左值bar
=]就好了。
如果你想要完美的转发,模板参数必须是你想要传递的右值限定类型,这意味着你几乎总是必须让函数本身被模板化。但是,帮自己一个忙,给它取个不同的名字。 T
和 T_
将是 不同类型 。
template <typename U>
void buz(U&& t) {
printme2(std::forward<U>(t));
}
如果你想要SFINAE,你也可以添加:
template <typename U,
typename allowed=typename std::enable_if<std::is_constructible<Bar,U>::value,void*>::type
>
void buz(U && t) {
printme2(std::forward <U> (t));
}