模板专业化和完美的转发

Template specialization and perfect forwarding

本文关键字:转发 完美 专业化      更新时间:2023-10-16

,所以我正在尝试构建"消毒器"功能以滤除格式的参数,然后再将其转发到 printf

template<typename A>
_CR_INLINE decltype(auto) sanitize_forward(A&& arg) {
    return std::forward<A>(arg);
}
template<>
_CR_INLINE decltype(auto) sanitize_forward<std::string>(std::string&& arg) {
    return std::forward<const char*>(arg.c_str());
}

因此,每个 std::string都应该"衰减"到 const char*中,以便正确格式化。

template<typename...Args>
_CR_INLINE void printf_safe(const std::string& format, Args&&...args) {
    std::printf(format.c_str(), sanitize_forward<Args>(args)...);
}

我希望参数完美转发到 printf,这就是为什么我返回 std::forward的原因。但是我不能真正围绕如何实现这一点。

1)decltype(auto)正确吗?它应该保留std::forward返回的R值参考性,对吗?

2)我应该如何专业化模板: std::string&&std::string

3)推导的转发参考应与实际类型相同,对吧?

您几乎完全误解了转发参考;这是可以预料的,它们会令人困惑。

要理解它们,您必须了解参考折叠规则,std::forward本身,DEMATED T&&时的模板参数扣除规则,以及它们至少在某种程度上如何将它们结合在一起。

首先,参考崩溃。查看此图表:

If       Then      Then
X is     X& is     X&& is
T        T&        T&&
T&       T&        T&
T const& T const&  T const&
T&&      T&        T&&

如果Xint,则X&&int&&。如果Xint&,则X&&int&

重读。再次。再一次。&在应用两者时都胜出&&

接下来,推论。我会在这里说谎,但他们正在简化谎言。

如果将Foo&传递到DEADUC X&&的模板,则XFoo&X&&Foo&

如果将Foo&&传递到DEADUC X&&的模板,则XFooX&&Foo&&

接下来,std::forward是一个有条件的举动。对于参考变量X&& xstd::forward<X>(x)decltype(x)(x)-它将x施加到被声明为类型的类型。如果x是RVALUE参考,则将x投入到RVALUE参考。这是需要的,因为即使x是RVALUE参考,即使x表达式CC_41的类型也不是X&&。rvalue参考是 to rvalue,但不是rvalue。

现在修复您的代码。


template<class T>
struct tag_t{constexpr tag_t(){}};
template<class T>
constexpr tag_t<std::decay_t<T>> tag{};
template<class T>
auto sanitizer(tag_t<T>){
  return [](auto&& t)->decltype(auto){
    return decltype(t)(t);
  };
}
template<class A>
decltype(auto) sanitize(A&& arg) {
  return sanitizer(tag<A>)(std::forward<A>(arg));
}
auto sanitizer(tag_t<std::string>) {
  return [](std::string const& s){return s.c_str();};
}
template<class...Ts>
void printf_safe(const std::string& format, Ts&&...ts) {
  std::printf(format.c_str(), sanitize(std::forward<Ts>(ts))...);
}

我遵循SRP(单个责任原则) - 我从消毒中向前拆下。

然后,我将哪个消毒动作(sanitizer)分开(sanitize)。

这让我写了一次 std::string santization代码,而无需"偶然"赢得完美的胜利。

顺便说一句,如果要将Array Arg视为非分子,我将用Remove Ref和删除CV替换。

您可以通过在tag_t的命名空间或我们正在消毒的类型的命名空间中编写sanitizer过载来扩展sanitize功能(自然而然地在std中)。

所以我正在尝试构建"消毒器"功能,以在将其转发到printf之前过滤格式化参数。

您的前提似乎是错误的。printf是使用va_arg的C函数 - 它不做或需要任何完美的转发。

http://en.cppreference.com/w/cpp/io/c/fprintf

它也永远不会"消耗"其论点,而只是从中读取。区分临时工和非陪伴是没有意义的 - 只需在printf包装中服用const Args&...


1)decltype(auto)正确吗?它应该保留std::forward返回的R值参考性,对吗?

std::forward将返回 lvalue参考 rvalue参考decltype(auto)确实会保留。

您对std::stringsanitize_forward的专业化看起来并不有用 - std::forward<const char*>将始终返回const char* &&。我认为这与:

template<>
_CR_INLINE const char* sanitize_forward<std::string>(std::string&& arg) {
    return arg.c_str();
}

此外,将 rvalue参考的 .c_str()返回到 std::string听起来很危险和不正确:您正在将即将到期的字符串的内部缓冲区进行指针。您可能想在这里服用const std::string&


2)我应该如何专业化我的模板:std::string&&std::string

它将如何称呼?您是否明确提供模板参数?模板参数是始终是 non-reference ,还是将既是 lvalue参考又是 non-non-Reference

由于您有sanitize_forward<Args>,您可能会尝试调用这两个...

  • sanitize_forward<std::string>

  • sanitize_forward<std::string&>

...也许使用 cv-qualifiers 。您可能需要提供额外的明确std::decay_t<Args>参数,该参数涉及"专业"业务。


3)推导的转发参考应与实际类型相同,对吧?

不确定您的意思。您能详细说明吗?