C++ 使用元编程为右值/左值引用生成函数签名?

C++ 使用元编程为右值/左值引用生成函数签名?,c++,c++17,C++,C++17,假设我想把右值和左值都传递给我的函数,但我不想写所有的O(2n)函数签名,其中n是参数的个数。例如,对于my String append函数: inline static String append(String&& l1, String&& l2) { l1.append(l2); return l1; } inline static String append(String&& l1, String& l2) {

假设我想把右值和左值都传递给我的函数,但我不想写所有的O(2n)函数签名,其中n是参数的个数。例如,对于my String append函数:

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));
}
内联静态字符串追加(字符串和l1、字符串和l2){
l1.追加(l2);
返回l1;
}
内联静态字符串追加(字符串和l1、字符串和l2){
l1.追加(std::forward(l2));
返回l1;
}
内联静态字符串追加(字符串和l1、字符串和l2){
l1.追加(l2);
返回l1;
}
内联静态字符串追加(字符串和l1、字符串和l2){
返回附加(std::forward(l1),std::forward(l2));
}
太多的函数签名了!TMP是一种图灵完整语言,必须有一种方法在编译时生成同一事物的所有4个版本,正确使用
std::forward
对吗?C++17魔法奖励积分。

您可以选择

template<class T, class S> static inline auto append(T&& l1, S&& l2) {
    l1.append(std::forward<S>(l2));

    return std::forward<T>(l1);
}
模板静态内联自动追加(T&&l1、S&&l2){
l1.追加(std::forward(l2));
返回std::转发(l1);
}
请注意,当实例化失败时,错误消息并不容易理解。这可以通过适当约束模板来缓解。

您可以使用

template<class T, class S> static inline auto append(T&& l1, S&& l2) {
    l1.append(std::forward<S>(l2));

    return std::forward<T>(l1);
}
模板静态内联自动追加(T&&l1、S&&l2){
l1.追加(std::forward(l2));
返回std::转发(l1);
}
请注意,当实例化失败时,错误消息并不容易理解。这可以通过适当地约束模板来缓解。

类似这样的内容:

// 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);
}

除此之外,您还可以约束
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) { ... }
模板
内联静态字符串追加(L&&L,R&&R){…}
类似这样的内容:

// 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);
}

除此之外,您还可以约束
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) { ... }
模板
内联静态字符串追加(L&&L,R&&R){…}

您可以自动推断参考类型:

template<typename Str>
std::remove_reference_t<Str> append(Str &&, Str &&);

至少它不是二次的。很少有typedef会压缩整个内容。

您可以自动推断参考类型:

template<typename Str>
std::remove_reference_t<Str> append(Str &&, Str &&);


至少它不是二次的。很少有typedef会压缩整个过程。

您在这里误用了
forward
。更具体地说,在您的情况下,它充当
std::move
。它旨在与通用引用一起使用……std::forward不是std::move还是no op吗?我怎么会误用它?是的。但是,由于您不使用通用引用,所以在您的情况下,它总是移动。您在这里误用了
向前
。更具体地说,在您的情况下,它充当
std::move
。它旨在与通用引用一起使用……std::forward不是std::move还是no op吗?我怎么会误用它?是的。但是,由于您不使用通用引用,在您的情况下,它总是移动。嗯,我记得看到t&&特别神奇,因此不可能将我的参数专门化为字符串并获得完美的转发?您真的应该约束模板。嗯,我记得看到t&&特别神奇,因此,不可能将我的参数专门化为字符串并获得完美的转发?你真的应该约束模板。继承不是问题,事实上,正如我在评论中所说的,我需要字符串、字符串和字符串的严格类型。我真的很喜欢你的最后一个技巧,在编译时投机性地调用append!总而言之,你必须在完美转发和功能多态性之间做出选择,这是一个真正的麻烦。我希望C++20能以一种更自然的方式解决这个问题。@rausted我不确定你所说的“函数多态性”是什么意思,或者这里有什么问题?如果你有一个
append(字符串a,字符串b)
append(列表a,列表b)
函数多态性允许根据类型签名和参数找到正确的一个。到目前为止,这在通用引用中是不可能的,因为
T&&
是一个通用的,我们必须使用TMP hacks来实现这一点,就像您的答案中的一样。我宁愿使用多态性而不是SFINAE。继承不是一个问题,事实上就像我在评论中所说的,我想要String、String&和String&&上的严格类型。我真的很喜欢你的最后一个技巧,在编译时投机性地调用append!总而言之,你必须在完美转发和功能多态性之间做出选择,这是一个真正的麻烦。我希望C++20能以一种更自然的方式解决这个问题。@rausted我不确定你所说的“函数多态性”是什么意思,或者这里有什么问题?如果你有一个
append(字符串a,字符串b)
append(列表a,列表b)
函数多态性允许根据类型签名和参数找到正确的一个。到目前为止,这在通用引用中是不可能的,因为
T&&
是一个通用的,我们必须使用TMP hacks来实现这一点,就像您的答案中的一样。我宁愿使用多态性而不是SFINAE。这不是通用的enought,它不能接受
const String&
String
这不是通用的enought,它不能接受
const String&
String