【问题标题】:How to get C++-template-like behavior but only allow two specific types to be instantiated?如何获得类似 C++ 模板的行为,但只允许实例化两种特定类型?
【发布时间】:2017-06-08 03:50:18
【问题描述】:

我正在尝试从内联 C++ 函数中获取特定类型的行为,但我不确定是否有办法做到这一点。

我希望我的函数接受有符号或无符号 16 位值作为参数,对该值执行操作,然后返回相同类型的值。如果参数的有符号/无符号是不明确的(例如,因为它是一个常量),那么编译器可以只选择有符号的版本。这是一个玩具程序,展示了我第一次尝试获得这种行为:

 #include <iostream>
 #include <cstdint>

  int16_t getValuePlusOne( int16_t x) {return x+1;}
 uint16_t getValuePlusOne(uint16_t x) {return x+1;}

 using namespace std;

 int main(int, char **)
 {
     int16_t   signedVal = -15;
    uint16_t unsignedVal = 23;

    cout << getValuePlusOne(  signedVal) << endl;  // works, yay!
    cout << getValuePlusOne(unsignedVal) << endl;  // works, yay!
    cout << getValuePlusOne(1234)        << endl;  // COMPILE ERROR, ambiguous!  D'oh!
    return 0;
 }

所以这几乎可以工作,但它在 getValuePlusOne(1234) 上出错,因为 1234 是不明确的(它可以是有符号或无符号的)。很公平,但我不希望它这样做。

这是我的第二次尝试:

#include <iostream>
#include <cstdint>

template <typename T> T getValuePlusOne(T val) {return val+1;}

using namespace std;

int main(int, char **)
{
    int16_t   signedVal = 5;
   uint16_t unsignedVal = 5;

   cout << getValuePlusOne(  signedVal) << endl;  // works, yay!
   cout << getValuePlusOne(unsignedVal) << endl;  // works, yay!
   cout << getValuePlusOne(1234)        << endl;  // works, yay!

   uint32_t inappropriateType32 = 54321;
   cout << getValuePlusOne(inappropriateType32) << endl;  // works, but I want this to be a compile-time error!  D'oh!

   float inappropriateTypeFloat = 666.0;
   cout << getValuePlusOne(inappropriateTypeFloat) << endl;  // works, but I want this to be a compile-time error!

   return 0;
}

这个版本的工作方式正是我希望它在前三个调用 getValuePlusOne() 时的工作方式——它们编译时没有错误,并且模板机制确保 getValuePlusOne() 的返回类型与其参数类型匹配,并选择一个模棱两可的情况下的默认参数/返回类型。耶!

但是——这个版本还允许用户传入在我的应用程序上下文中没有意义的不适当的值(例如 32 位整数,甚至——gasp——浮点类型),所以我希望编译器将这些调用标记为编译时错误,而此实现不会发生这种情况。

有什么方法可以让我把蛋糕也吃掉吗?

【问题讨论】:

  • 请注意,1234 的类型为 int。如果你想让getValuePlusOne(1234) 工作,那么int x = 1234; getValuePlusOne(x); 也可以工作。
  • 您可以将函数模板设置为= delete; 以禁用来自您未明确启用的任何类型的调用

标签: c++ templates


【解决方案1】:

这样的事情呢?

template <typename T> T getValuePlusOne(T val)
{
    static_assert(std::is_same<T, int16_t>::value || std::is_same<T, uint16_t>::value, "Incorrect type");
    return val+1;
}

不过,getValuePlusOne(1234) 还是失败了。但这并不是因为类型不明确。这是因为Tint

【讨论】:

    【解决方案2】:

    您可以使用 sfinae 将函数限制为符合某些条件的参数:

    template<typename T>
    constexpr bool is_any_int16 = std::is_same<T, int16_t>::value || std::is_same<T, uint16_t>::value;
    
    template<typename T>
    auto getValuePlusOne(T x) -> std::enable_if_t<is_any_int16<T>, T> {
        return x + 1;
    }
    

    【讨论】:

      【解决方案3】:

      我的方法是制作一个模板函数并将其拆分为头文件和 cpp 文件。这样你必须提供允许的类型,否则你会得到一个编译错误:

      main.cpp:

      #include <iostream>
      #include "GetValuePlusOne.h"
      
      int main() {
          int i = 2;
          std::cout << GetValuePlusOne(i) << std::endl; 
      
          return 0;
      }
      

      GetValuePlusOne.h:

      #pragma once
      
      template <typename T>
      T GetValuePlusOne(T value);
      

      GetValuePlusOne.cpp:

      #include <cstdint>
      
      template <typename T>
      T GetValuePlusOne(T value) {
          return value + 1;
      }
      
      template int GetValuePlusOne<int>(int);
      template int16_t  GetValuePlusOne<int16_t>(int16_t );
      template uint16_t   GetValuePlusOne<uint16_t >(uint16_t  );
      

      正如 Igor 已经指出的,您需要一个“int”模板来编译您的“1234”示例。希望这可行,无法测试。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多