通过元编程为右值/左值引用生成函数签名?

时间:2018-08-13 14:43:19

标签: c++ c++17

比方说,我想将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魔术的加分。

3 个答案:

答案 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可以压缩整个内容。