【问题标题】:How can I get C++ to infer my template typename parameter?如何让 C++ 推断我的模板类型名参数?
【发布时间】:2021-11-05 18:58:34
【问题描述】:

我想编写一个类型别名模板,它可以解析为大多数类型的模板参数的 const,如下所示:

template <typename T>
using TypeAlias = const T;

但对于特定类型,比如 int,它只是解析为 T。

我已经尝试使用 std::conditional 以及包含类型别名的结构的特化来执行此操作,但在所有情况下,编译器都无法推断类型。我的问题是:在下面的示例中我做错了什么?以及如何正确地做到这一点?

编辑:我在一个非常大的代码库中工作,该代码库处理大量类型,并在 TypeAlias 的定义中为所有类型添加了 const(这实际上是一种更复杂的类型,基本上是一个基于 const T 模板化的容器)。我正在尝试修改此代码库以接受不能为 const 的新类型,同时进行最少的修改。在 foo 等所有模板化函数中显式指定类型不是一个可行的解决方案。我真正在寻找的是一种修改 TypeAlias 的方法,几乎​​没有其他任何东西。

示例 1:

#include <stdio.h>
#include <type_traits>

template <typename T>
using TypeAlias = typename std::conditional<std::is_same<int, T>::value,
      T, typename std::add_const<T>::type>::type;

template <typename T>
void foo(TypeAlias<T> var) {
  printf("var = %f\n", static_cast<double>(var));
}

int main(int argc, char *argv[]) {
  const int a = 1;
  const float b = 2;
  const double c = 3;

  foo(a);
  foo(b);
  foo(c);

  return 0;
}

示例 2:

template <typename T>
struct TypeHolder {
  using type = const T;
};

template <>
struct TypeHolder<int> {
  using type = int;
};

template <typename T>
void foo(typename TypeHolder<T>::type var) {
  printf("var = %f\n", static_cast<double>(var));
}

int main(int argc, char *argv[]) {
  int a = 1;
  const float b = 2;
  const double c = 3;

  foo(a);
  foo(b);
  foo(c);

  return 0;
}

【问题讨论】:

  • 函数参数忽略顶级 const。所以你的努力没有效果。
  • 这是一个更复杂问题的简化,其中 TypeAlias 是 const T 或 Ts 的容器。虽然 const 可能对这个玩具问题没有影响,但它在原始问题中有效,并且无法推断类型的事实仍然存在。

标签: c++ templates type-inference


【解决方案1】:

这是另一种为特定类型集删除 const 的方法,只需添加一个重载并使用 SFINAE 根据推导的类型是否在集中进行约束:

#include <iostream>
#include <type_traits>

template <class T, class... Ts>
inline constexpr bool is_any_v = (... || std::is_same_v<T, Ts>);

template <class T>
inline constexpr bool int_or_short_v = is_any_v<T, int, short>;

template <class T>
std::enable_if_t<!int_or_short_v<T>> foo(const T) { std::cout << "const" << std::endl; }
template <class T>
std::enable_if_t<int_or_short_v<T>> foo(T) { std::cout << "non-const" << std::endl; }

int main() {
    const int a = 1;
    const short b = 2;
    const float c = 3;
    const double d = 4;

    foo(a);
    foo(b);
    foo(c);
    foo(d);
}

Try it on godbolt.org

在 C++20 中,更简单:

#include <iostream>
#include <concepts>

template <class T, class... Ts>
concept any_of = (... || std::same_as<T, Ts>);

void foo(const auto) { std::cout << "const" << std::endl; }
void foo(any_of<int, short> auto) { std::cout << "non-const" << std::endl; }

int main() {
    const int a = 1;
    const short b = 2;
    const float c = 3;
    const double d = 4;

    foo(a);
    foo(b);
    foo(c);
    foo(d);
}

Try it on godbolt.org

【讨论】:

    【解决方案2】:

    正如 cmets 中提到的,这是一个非推导上下文,因此不会推导模板类型。这实际上是您可以用来强制调用者明确设置模板参数的技巧之一。 你可以做的是使用if constexpr 来区分你的两个(或更多)案例:

    void foo_for_int(int x) { /* do something for ints */ }
    template <typename T> foo_for_others(T const x) { /* do something for other types */ }
    
    template <typename T>
    void foo(T && t)
    {
      if constexpr (std::is_same_v<int, T>)
      {
        foo_for_int(t); // no need to forward an int
      }
      else
      {
        foo_for_others(std::forward<T>(t));
      }
    }
    

    【讨论】:

    • 这属于哪个非推断上下文规则?
    猜你喜欢
    • 2023-03-31
    • 2015-02-06
    • 1970-01-01
    • 2020-06-25
    • 2018-03-29
    • 1970-01-01
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多