【问题标题】:C++ constraints on variadic template arguments可变参数模板参数的 C++ 约束
【发布时间】:2021-10-22 06:43:47
【问题描述】:

我在SerenityOS project 中偶然发现了这段代码:

template<typename... Parameters>
void dbgln(CheckedFormatString<Parameters...>&& fmtstr, const Parameters&... parameters)

他们正在从 rust 重新实现 println! 的等价物。一个更简单的 printf 版本,您不需要关心参数类型并以这种方式使用:

dbgln("This is a {}", "test");

他们正在对fmtstr 进行一些编译时检查,以确保没有未闭合的大括号并且大括号的数量与参数的数量相匹配。这就是为什么需要对 FormatString 结构进行模板化以在编译时访问参数数量的原因。

但是有一点我不明白。 I wrote 一个 MWE,其代码如下,本质上是重现他们正在做的事情:

#include <stddef.h>

template<typename... Args>
void compiletime_fail(Args...);

template<typename ...Parameters>
struct UnconstrainedFormatString {
template <size_t size>
  consteval UnconstrainedFormatString(const char (&buffer)[size]): m_buffer(buffer), m_size(size) {
  }

  const char *m_buffer { nullptr };
  const size_t m_size { 0 };
};

template<typename T>
struct __IdentityType {
  using Type = T;
};

template<typename T>
using IdentityType = typename __IdentityType<T>::Type;

template<typename... Args>
using FormatString = UnconstrainedFormatString<IdentityType<Args>...>; // but why?

template<typename ...Parameters>
constexpr void println(FormatString<Parameters...>&& fmtstr, const Parameters& ...parameters) {
}

int main() {
  println("this is a test", 1, 2, 3);
}

如果我在println 签名中使用UnconstrainedFormatString,我会从编译器收到此错误:

/cplayground/code.cpp:32:3: error: no matching function for call to 'println'
  println("this is a test", 1, 2, 3);
  ^~~~~~~
/cplayground/code.cpp:28:16: note: candidate template ignored: could not match 'UnconstrainedFormatString<type-parameter-0-0...>' against 'char const[15]'
constexpr void println(UnconstrainedFormatString<Parameters...>&& fmtstr, const Parameters& ...parameters) {

为了编译它,我需​​要用IdentityType 做这个时髦的事情。

我为什么需要这个?

【问题讨论】:

  • 为什么需要 UnconstrainedFormatString 作为模板?如果您将其定义为结构,则使用 UnconstrainedFormatString 一切都会正常工作。你不会把事情复杂化吗?
  • 我认为目的是在编译时检查大括号的数量是否与参数的数量相匹配。
  • 防止从该论点中扣除。见stackoverflow.com/questions/17433082/…
  • @ecatmur 我想你可能在这里有所收获。但是在我的示例中,不可能有歧义,因为不可能从格式字符串中推断出可变参数的类型。想详细说明一下,也许在答案中?
  • 好的,en.cppreference.com/w/cpp/language/… 解释了等同于 std::type_identity 的 IdentityType。但它的使用是为了避免模棱两可的类型推导,这不是这里的情况。错误是不同的。仍然需要了解错误的来源才能全面了解。

标签: c++ template-meta-programming type-deduction


【解决方案1】:

https://en.cppreference.com/w/cpp/language/template_argument_deduction#Implicit_conversions

类型推导不考虑隐式转换(除了类型 上面列出的调整):这就是重载解决的工作, 稍后会发生。

FormatString/IdentityType 如何改变事物? IdentityType 抑制了 const char[N] 的模板参数的推导,现在该字符串用作 UnconstrainedFormatString<...> 的构造函数参数em>。

您可以在此处了解更多详情: https://humanreadablemag.com/issues/0/articles/how-to-avoid-template-type-deduction-in-c

当您仅使用 UnconstrainedFormatString 时,编译器会尝试从 const char [N] 中扣除 UnconstrainedFormatString 并失败,因为对于模板它“没有'不知道'存在从 const char[N]UnconstrainedFormatString 的转换。

您可以轻松检查

println(UnconstrainedFormatString<int, int, int>("test"), 1, 2, 3);

由于这里不需要转换,所以它也可以工作。

【讨论】:

    猜你喜欢
    • 2021-09-04
    • 1970-01-01
    • 2017-08-04
    • 2016-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-18
    • 2021-03-08
    相关资源
    最近更新 更多