【问题标题】:In function template, how to determine type of one argument based on another在函数模板中,如何根据另一个参数确定一个参数的类型
【发布时间】:2021-04-04 20:48:02
【问题描述】:

我想实现一个函数模板,它接受两个参数,一个 T* 和一个 T,但第二个参数的类型由第一个参数决定。这是一个最小的非工作示例:

#include <cstddef>
#include <cstring>
#include <cstdint>
#include <type_traits>
#include <vector>

template<typename T> void
patch(T *dst, T src)
{
  static_assert(std::is_standard_layout_v<T>);
  std::byte *p = reinterpret_cast<std::byte *>(&src);
  std::vector newval(p, p + sizeof(src));

  // In the real code, memcpy happens later if a transaciton commits
  std::memcpy(dst, newval.data(), newval.size());
}

int
main()
{
  std::uint16_t u16;
  patch(&u16, 0);  // Fails to compile because 0 is int, not uint16_t
}

不幸的是,这段代码无法编译,因为无法在 patch(&amp;u16, 0) 中推断要修补的类型 T,因为 0 是 int 而不是 std::uint16_t。显然我可以投 0 或致电 patch&lt;uint16_t&gt;(...),但在理想情况下我不必这样做。

另一方面,如果第二个参数涉及某种非平凡的类型计算,我可以解决这个问题。例如,如果我将函数声明为:

template<typename T> void
patch(T *dst, std::decay_t<T> src) {/*...*/}

当我最初提出这个问题时,我实施了以下操作并认为它不起作用。但是,我一定犯了一个错误,因为正如所选答案所指出的那样,它确实有效:

template<typename T> struct sametype {
  using type = T;
};
template<typename T> using sametype_t = typename sametype<T>::type;

template<typename T> void
patch(T *dst, sametype_t<T> src) {/*...*/}

我的问题是可以对模板函数参数应用的最小转换是什么,以强制其类型由同一函数的不同参数的类型推断?

【问题讨论】:

    标签: c++ templates c++17 function-templates


    【解决方案1】:

    这正是std::type_identity_t&lt;T&gt; 打算解决的那种情况。这种转换创建了一个所谓的非推导上下文,您已经为T 类型的第二个参数请求了它。不幸的是,直到 C++20 标准才引入这种类型特征。

    你可以继续use the one you've implemented(顺便说一下,是的,它至少根据godbolt编译),或者如果你使用boost,有一个boost::type_identity_t&lt;T&gt;可用。

    不过,要直接回答您的问题,我相信 C++17 中最最小 必要的转换之一是 this:

    template<typename T> void
    patch(T *dst, std::enable_if_t<true, T> src)
    

    【讨论】:

      【解决方案2】:

      这是一个可接受的解决方案吗?

      template<typename T, typename U> void
      patch(T *dst, U src)
      {
        T s = src;
        static_assert(std::is_standard_layout_v<T>);
        std::byte *p = reinterpret_cast<std::byte *>(&s);
        std::vector newval(p, p + sizeof(T));
      
        // In the real code, memcpy happens later if a transaciton commits
        std::memcpy(dst, newval.data(), newval.size());
      }
      

      【讨论】:

      • 我的意思是我想到了这一点,但它在语义上并不相同,如果出现溢出可能会导致更少的编译器警告。想象一下,T 是一个标准布局类型,带有构造函数,而 U 没有复制构造函数。
      • 您可以使用const U &amp;src 跳过复制。
      • 但是你抑制了可能有用的编译器警告。例如,对于另一个答案,如果你写 patch(&amp;u16, 0x10000),那么 clang++ 和 g++ 都会给你一个警告,而你的只是编译。你的答案本来是我的后备方案,但显然std::type_identity 或同等答案是最干净的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-13
      • 1970-01-01
      • 2021-01-29
      相关资源
      最近更新 更多