【问题标题】:Uniform random distribution "base class" for both int and double?int 和 double 的均匀随机分布“基类”?
【发布时间】:2015-12-30 16:03:23
【问题描述】:

我正在尝试创建一个用随机数填充列表的函数,并根据列表项的类型生成整数或浮点数。到目前为止,我已经提出了以下代码,并且它可以工作:

template <typename T>
void generateRandom(list<T>& numberList){
    default_random_engine randomGenerator(random_device{}());

    if( typeid(T) == typeid(int) ){
        uniform_int_distribution<T> distribution(1000, 2000);
        auto myGenerator = bind(distribution, randomGenerator);
        generate(numberList.begin(), numberList.end(), myGenerator);
    }
    else if( typeid(T) == typeid(double) ){
        uniform_real_distribution<T> distribution(1000.0, 2000.0);
        auto myGenerator = bind(distribution, randomGenerator);
        generate(numberList.begin(), numberList.end(), myGenerator);
    }
    else{
        return;
    }

}

但是,我对这个解决方案并不满意。感觉应该有某种方法可以打破除该 IF 语句的实际分布之外的所有内容。比如:

template <typename T>
void generateRandom(list<T>& numberList){
    default_random_engine randomGenerator(random_device{}());

    X distribution;
    if( typeid(T) == typeid(int) )
        distribution = uniform_int_distribution<T>(1000, 2000);
    else if( typeid(T) == typeid(double) )
        distribution = uniform_real_distribution<T>(1000.0, 2000.0);
    else
        return;

    auto myGenerator = bind(distribution, randomGenerator);
    generate(numberList.begin(), numberList.end(), myGenerator);
}

(X 是此处缺少的占位符)

但是由于uniform_int_distributionuniform_real_distribution 没有共同的基类,那怎么可能呢?我尝试过使用函数指针和仿函数,但我似乎从来没有超过那个 IF 语句,这很烦人。这可以用更优雅的方式解决吗?

注意:这是学校作业的一部分,我们必须使用generate()&lt;random&gt; 函数和模板来生成随机数。我认为我的原始解决方案是可以接受的,我只是不喜欢 IF 语句中那些重复的代码行的外观...... :)

【问题讨论】:

    标签: c++ random function-pointers distribution functor


    【解决方案1】:

    你可以创建一个类似 typetrait 的类:

    template<class T>
    struct Distribution {};
    
    template<>
    struct Distribution<int> {
        typedef uniform_int_distribution<int> type;
    };
    
    template<>
    struct Distribution<double> {
        typedef uniform_real_distribution<double> type;
    };
    
    template <typename T>
    void generateRandom(list<T>& numberList){
        default_random_engine randomGenerator(random_device{}());
    
        typename Distribution<T>::type distribution = typename Distribution<T>::type(1000, 2000);
        ...
    

    另一种选择是创建一个模板辅助函数,该函数将接受所需的分布作为参数:

    template<class T, template<class> class Distr>
    void generateRandomHelper(list<T>& numberList){
        default_random_engine randomGenerator(random_device{}());
    
        Distr<T> distribution = Distr<T>(1000, 2000);
    
        auto myGenerator = bind(distribution, randomGenerator);
        generate(numberList.begin(), numberList.end(), myGenerator);
    }
    
    template<class T>
    void generateRandom2(list<T>& numberList);
    
    template<>
    void generateRandom2<int>(list<int>& numberList) {
        generateRandomHelper<int, uniform_int_distribution>(numberList);
    }
    
    template<>
    void generateRandom2<double>(list<double>& numberList) {
        generateRandomHelper<double, uniform_real_distribution>(numberList);
    }
    

    【讨论】:

      【解决方案2】:

      你可以使用:

      template <typename T>
      using my_distribution = std::conditional_t<std::is_integral<T>::value,
                         std::uniform_int_distribution<T>,
                         std::conditional_t<std::is_floating_point<T>::value,
                                            std::uniform_real_distribution<T>,
                                            void>
                         >;
      

      然后

      template <typename T>
      void generateRandom(list<T>& numberList){
          default_random_engine randomGenerator(random_device{}());
          my_distribution<T> distribution(1000, 2000);
          auto myGenerator = bind(distribution, randomGenerator);
          generate(numberList.begin(), numberList.end(), myGenerator);
      }
      

      【讨论】:

      • 我建议使用std::is_integralstd::is_floating_point 而不是std::is_same。恕我直言,更通用且更易于理解。
      • 我没有编译这个。对于初学者,conditional_t 无法识别。当我将其更改为conditional 时,那部分就可以了。但是随后声明分布变量的行得到 "No matching constructor for initialization of 'my_distribution'" 错误。不知道如何解决?
      • std::contitional_t&lt;Cond, T1, T2&gt; (C++14) 应替换为 typename std::contitional&lt;Cond, T1, T2&gt;::type (C++11)(或者您可以创建自己的别名)。
      • 好的,已经成功了,谢谢!得到的答案比我希望的要多,但我最喜欢这个。我想这就是我的想法,只是不知道有conditional&lt;&gt;这样的东西。
      【解决方案3】:

      您正在尝试计算类型。这需要(非常、非常、非常)简单的模板元编程:

      template<typename T> class Distribution;
      template<> class Distribution<int> {
          using distribution = uniform_int_distribution<int>;
      };
      template<> class Distribution<double> {
          using distribution = uniform_real_distribution<int>;
      };
      
      
      template <typename T>
      void generateRandom(list<T>& numberList){
          default_random_engine randomGenerator(random_device{}());
      
          Distribution<T>::distribution distribution(1000, 2000);
      
          auto myGenerator = bind(distribution, randomGenerator);
          generate(numberList.begin(), numberList.end(), myGenerator);
      }
      

      当 T 既不是 int 也不是 double 时,此代码不会编译。编译器会抱怨,你的 T 没有类型 Distribution&lt;T&gt;::distribution

      假设,默默地什么都不做,例如对于 float 或 unsigned int,确实是您需要的:

      template<typename T> class Distribution {
      public:
          // just anything, so that there is a type
          // not sure if this is really necessary
          using distribution = uniform_int_distribution<int>; 
          static const bool valid = false;
      };
      template<> class Distribution<int> {
      public:
          using distribution = uniform_int_distribution<int>;
          static const bool valid = true;
      };
      template<> class Distribution<double> {
      public:
          using distribution = uniform_real_distribution<double>;
          static const bool valid = true;
      };
      
      
      template <typename T>
      void generateRandom(list<T>& numberList){
          default_random_engine randomGenerator(random_device{}());
      
          if(!Distribution<T>::valid) return;
      
          Distribution<T>::distribution distribution(1000, 2000);
          auto myGenerator = bind(distribution, randomGenerator);
          generate(numberList.begin(), numberList.end(), myGenerator);
      }
      

      现在默认设置只是默默地什么都不做。要实际生成除 int 或 double 之外的随机数,您必须针对您的类型专门化模板,就像我为 int 和 double 所做的那样。

      【讨论】:

      • Distribution&lt;T&gt;::distribution distribution;前面不需要写typename吗?
      • 您尝试过您提出的解决方案吗?
      • @5gon12eder 没有。我希望 BadCash 能够了解我的解决方案的概念,而不仅仅是尝试从其他人那里复制粘贴。这不是廉价代码猴子的来源,而是学习的地方。
      • @cdonat 这不是宣传损坏代码的借口。如果你想留下一些东西让提问者咀嚼,你不需要展示一个完整的工作解决方案,但如果你发布代码,它应该是正确的。你的有很多错误。除了 nwp 和 Jarod42 已经提到的那些,你的 trait class 的成员都是 private。虽然一开始忽略未知类型可能不是最好的主意,但您会发现它也不能像这样工作,因为即使您很早就return,您的整个函数体也必须正确输入。你需要另一个 SFINAE 层。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-08
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      • 1970-01-01
      • 2020-04-04
      • 1970-01-01
      相关资源
      最近更新 更多