【问题标题】:How to return correct type of data in templates?如何在模板中返回正确类型的数据?
【发布时间】:2020-05-02 12:34:34
【问题描述】:
#include <iostream>
using namespace std;

template <class X, class Y>
Y big(X a, Y b)
{
   if (a > b)
      return (a);
   else return (b);
}

int main()
{
   cout << big(32.8, 9);
}

这里我在 CPP 中使用模板,所以当我绕过 doubleint 类型的参数调用函数 big 时,我想要返回答案是 double。这里的类型,它返回32而不是32.8

如何获得我想要的输出? 如何编写正确的big函数返回类型?

【问题讨论】:

  • 一个函数只能返回一个固定类型。您不能在运行时选择要返回的类型。
  • 您可能想看看std::max 是如何实现的。函数的返回类型必须在 C++ 的编译时知道。所以你不能让这个返回类型依赖于参数的运行时值。这就是为什么对于这样的函数,您需要两个参数具有相同的类型(即具有类型 X,但不是 Y)。

标签: c++ function templates return-type function-templates


【解决方案1】:

在将返回类型标记为Y 并将int 作为第二个参数传递时,您已经清楚地表明Yint。这里没有什么意外。

#include <iostream>

template <typename X, typename Y>
decltype(auto) big(const X& a, const Y& b)  // return type can just be auto as well 
{
    return a > b ? a : b;
}

int main()
{
    std::cout << big(32.8, 9) << '\n';
    std::cout << big(9, 32.8) << '\n';
    std::cout << big(32.8, 90) << '\n';
    std::cout << big(90, 32.8) << '\n';
}

这会将所有四个正确的值打印到屏幕上。

https://godbolt.org/z/fyGsmo

需要注意的重要一点是,这仅适用于可以相互比较的类型,即编译器会将一种类型隐式转换为另一种类型以进行比较。

重要提示:参数需要通过引用来避免未定义的行为。这与我一直坚持的返回类型有关。 decltype(auto) 可以返回对类型的引用。如果你返回函数的局部值(参数计数),你会得到未定义的行为。

【讨论】:

  • @walnut 不小心返回一个参考比这个网站说的要困难得多。但是很高兴了解未定义的行为。无论如何,这不是我要编写的代码;这是一个问题的答案。
  • 啊。我将您之前的评论视为两个不同的观点,而不是结果和原因。我可以进行适当的编辑。
  • 我添加了额外的免责声明。
【解决方案2】:

返回类型必须在编译时确定。 如果您仅限于 ,则可以将 trailing returnconditional operator 一起使用。

template <typename X, typename Y>
auto big(X&& a, Y&& b) -> decltype(a > b ? a : b) // ---> like this
{
   return  a > b ? a : b;
}

See live


但是,如果您有权访问 或更高级别的auto,则返回就足够了,因为如果您将其与条件运算符一起使用,编译器将推断出正确的类型,如下所示:

template <typename X, typename Y>
auto big(X a, Y b)
{
   return  a > b ? a : b;
}

See live

【讨论】:

  • 不需要尾随返回类型,至少从 C++14 开始。
  • @JeJo 是的,我想这也很好,但可能毫无意义,因为您没有修改任何一个参数,并且在任何一种情况下返回类型仍然是左值引用(尽管可能不是@ 987654330@).
  • 我删除了我的 cmets,因为它们不再适用,但我建议在答案中添加一个警告,即您不能按值获取参数。
  • 如果有人查看您的代码,似乎传递的参数将决定某人将获得哪种返回类型,而不是的情况!即使 a 比 b 大,你总是会得到双倍的。
【解决方案3】:

这很可能不是针对您的确切情况的正确解决方案 - 其他答案可能更接近您想要的。

但是,如果您真的出于某种原因需要在运行时返回完全不同的类型,正确的解决方案(因为)是使用std::variant,这是一种类型-安全联合。

#include <variant>

template <typename X, typename Y>
std::variant<X, Y> max(X a, Y b) {
  if (a > b)
    return std::variant<X, Y>(std::in_place_index_t<0>, a);
  else
    return std::variant<X, Y>(std::in_place_index_t<1>, b);
}

请注意,调用者有责任处理返回的值,最有可能使用std::visit 等。

【讨论】:

    【解决方案4】:

    它返回 int 因为 Y 是一个 int 并且它将 32.8 转换为它。当你叫大 32,82是float,但8是int,函数返回类型是Y,也是int。

    你不能真正解决这个问题,因为你需要在运行时知道哪个类型大返回,所以像这样使 a 和 b 成为相同的类型:

        #include <iostream>
        using namespace std;
    
        template <typename X>
    
        X big (X a, X b)
        {
        if (a>b)
        return a;
    
        else return b;
        }
    
        int main()
        {
        cout<< big (32.8, 9.0);
        }
    

    【讨论】:

      【解决方案5】:

      一个函数只能有一个在编译时必须知道的返回类型。但是,您可以使用std::common_type 来返回两个参数都可以隐式转换为的类型。

      那就是

      #include <type_traits>
      template <class X, class Y>
      typename std::common_type<X,Y>::type big(X a, Y b)
      {
         if (a > b)
            return a;
         else return b;
      }
      

      要检查它在传递intdouble 时是否真的返回double,我们可以这样做:

      int main() {
          auto x = big(4.2,42);
          std::cout << std::is_same<decltype(x),double>::value;
      }
      

      打印出来的

      1
      

      PS:std::common_type 可以在幕后使用三元运算符,因此该解决方案与其他答案(auto + 三元)没有太大区别。 std::common_type 的真正强大之处在于它可以接受任意数量的参数。

      【讨论】:

        猜你喜欢
        • 2021-12-11
        • 2023-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-20
        • 1970-01-01
        • 2020-09-13
        • 1970-01-01
        相关资源
        最近更新 更多