比方说,我想将rvalue和lvalues都传递给函数,但是我不想写所有O(2 n )函数签名,其中n是参数的数量。例如,对于我的String追加函数:
inline static String append(String&& l1, String&& l2) {
l1.append(l2);
return l1;
}
inline static String append(String&& l1, String& l2) {
l1.append(std::forward<String>(l2));
return l1;
}
inline static String append(String& l1, String&& l2) {
l1.append(l2);
return l1;
}
inline static String append(String &l1, String& l2) {
return append(std::forward<String>(l1), std::forward<String>(l2));
}
功能签名太多了! TMP是一种图灵完整的语言,必须有一种方法可以在编译时正确使用std::forward
来生成同一事物的所有4个版本,对吗? C ++ 17魔术的加分。
答案 0 :(得分:4)
类似这样的东西:
// this will be in C++20
template <typename T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename L=String, typename R=String,
std::enable_if_t<
std::is_same_v<std::remove_reference_t<L>, String> &&
std::is_same_v<remove_cvref_t<R>, String>,
int> = 0>
inline static String append(L&& l, R&& r) {
l.append(std::forward<R>(r));
return std::forward<L>(l);
}
默认模板参数用于允许 braced-init-list 之类的东西。不同的约束是因为R
可以引用const String
,而L
不能。
根据您的预期用途,这可能并不完全正确,因为它会阻止从String
传入派生的类型-而原始重载则不能。如果您不在乎,那很好。如果这样做,那么您要改为检查is_base_of
:
std::is_base_of_v<String, remove_cvref_t<L>> &&
std::is_base_of_v<String, remove_cvref_t<R>> &&
!std::is_const_v<std::remove_reference_t<L>>
或者,您可以限制append
本身的格式:
template <typename L=String, typename R=String,
typename = decltype(std::declval<L&>().append(std::declval<R>())),
std::enable_if_t<std::is_convertible_v<L, String>, int> = 0>
inline static String append(L&& l, R&& r) { ... }
答案 1 :(得分:0)
你可以和
一起去template<class T, class S> static inline auto append(T&& l1, S&& l2) {
l1.append(std::forward<S>(l2));
return std::forward<T>(l1);
}
请注意,当实例化失败时,错误消息并不是很容易掌握。可以通过适当限制模板来缓解这种情况。
答案 2 :(得分:0)
您可以自动推断引用类型:
template<typename Str>
std::remove_reference_t<Str> append(Str &&, Str &&);
如果您希望它精确地为String
,那么
template<typename Str>
std::enable_if_t<
std::is_same_v<std::remove_reference_t<Str>, String>
, String>
append(Str &&, Str &&);
至少它不是二次方的。很少有typedef可以压缩整个内容。