【问题标题】:Return different types with different template parameter value (but same type)返回具有不同模板参数值的不同类型(但类型相同)
【发布时间】:2019-12-18 22:09:15
【问题描述】:

我想做的是定义3个这样的函数:

template<int t = 0> int test() { return 8; }
template<int t = 1> float test() { return 8.8; }
template<int t = 2> std::string test() { return "8.9"; }

int main()
{
    int a = test<0>();
    float b = test<1>();
    std::string c = test<2>();
    return 0;
}

它们使用相同类型的模板参数,但返回不同的类型。

我相信一定有某种方法可以做到这一点(比如std::get&lt;&gt;() 做到了),但我找不到如何做到这一点。

【问题讨论】:

    标签: c++ templates template-specialization return-type


    【解决方案1】:

    在我看来,您正在使用函数模板专业化。需要为每个调用提供不同的实现符合要求。但是有一个警告,那就是特化可能不会改变被特化的主模板的签名,只会改变实现。这意味着我们不能做例如

    template<int t> int test(); // Primary
    template<> int test<0>() { return 8; } // OK, signature matches
    template<> float test<1>() { return 8.8; } // ERROR
    

    但我们还没有被烤熟。专业化的签名必须与主要针对特定​​参数获得的签名相匹配。因此,如果我们使返回类型依赖模板参数,并解析为正确的类型,我们就可以很好地定义我们的特化。

    template<int t> auto test() -> /* Magic involving t that resolves to int, float, string */;
    template<> int test<0>() { return 8; }
    template<> float test<1>() { return 8.8; }
    template<> std::string test<2>() { return "8.9"; }
    

    这样的东西存在吗?是的,你暗示过。我们可以使用std::tuple。它有一个std::tuple_element 实用程序,可以将整数映射到类型序列之一(元组的元素)。借助一个小助手,我们可以按照您希望的方式构建代码:

    using types = std::tuple<int, float, std::string>;
    
    template<int t> auto test() -> std::tuple_element_t<t, types>;
    
    template<> int test<0>() { return 8; }
    template<> float test<1>() { return 8.8; }
    template<> std::string test<2>() { return "8.9"; }
    

    现在每个特化都与主要的签名相匹配。所以我们得到了编译器的批准。

    See it live

    【讨论】:

      【解决方案2】:

      @StoryTeller@formerlyknownas_463035818 提供了一种很好解释的模板专业化方法。或者,可以使用 if-constexpr 将三个函数组合成一个函数,并在 中返回 decltype(auto)

      #include <iostream>
      #include <string>
      #include <cstring>
      using namespace std::literals;
      
      template<int t>
      constexpr decltype(auto) test() noexcept
      {
          if constexpr (t == 0) return 8;
          else if constexpr (t == 1) return 8.8f;
          else if constexpr (t == 2) return "8.9"s;
      }
      

      (See live online)

      【讨论】:

        【解决方案3】:

        您声明了 3 次相同的模板,而您实际上想要专业化。据我所知,您不能直接专注于返回类型(*)。但是,没有什么是额外的间接层无法解决的。您可以按照以下方式进行操作:

        #include <string>
        
        template <int> struct return_type_tag {};
        template <> struct return_type_tag<0> { using type = int; };
        template <> struct return_type_tag<1> { using type = float; };
        template <> struct return_type_tag<2> { using type = std::string; };
        
        template <int x> typename return_type_tag<x>::type test();
        
        template<> int test<0>() { return 8; }
        template<> float test<1>() { return 8.8; }
        template<> std::string test<2>() { return "8.9"; }
        
        int main()
        {
            int a = test<0>();
            float b = test<1>();
            std::string c = test<2>();
        
            return 0;
        }
        

        (*) 实际上你可以通过一个小技巧查看this answer。我的方法的唯一好处是它已经在 c++11 之前工作了。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-04-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-10
          • 2014-08-19
          相关资源
          最近更新 更多