Function template parameters failing to convert ty

2019-08-27 23:34发布

问题:

While trying to use a function template that calls a class's specialized static function template it is failing to convert its parameter from the template parameter list.

Here is the function that I'm calling in main:

template<class Engine, typename Type, template<typename = Type> class Distribution, class... DistParams>
Type randomGenerator( RE::SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list, DistParams... params ) {
    static Type retVal = 0;
    static Engine engine = RE::getEngine<Engine>( seedType, seedValue, list );
    static Distribution<Type> dist = RD::getDistribution<Type, Distribution>( params... );
    retVal = dist( engine );
    return retVal;
}

A little bit about the function above:

The Type represents the return value of the distribution such as int, unsigned, char, etc for Integral Types that work with distributions such as uniform_int_distribution<IntType> or Real Types such as float, double for distributions like uniform_real_distribution<RealType>.

The terms RE & RD are typedefs of two classes.

typedef RandomEngine RE;
typedef RandomDistribution RD;

Both classes follow the same pattern as they have delete constructors and all of their methods are declared a static.

The 2nd line in the function is using the template parameter <class Engine> to represent what kind of engine from the <random> header file we want to use from the static methods in class RandomEngine{...}. Each of the engine types has it's own function to create an engine, seed it by the seeding type and seed values and then returns a reference of the engine. All of the functions in the RandomEngine class are non template functions. So I then went ahead and made generalized function template RE::getEngine<Engine>( parameters ); in this RandomEngine class that you can see in this function template randomGenerator(). Then I specialized this function for each engine type. I had no problems with doing that.

This brings me to the next line with the RandomDistribution class I'm trying to follow a similar pattern as I did with the RandomEngine I made a generalized function template RD::getDistribution<Type, Distribution>( params... );


Before I get to the RD::getDistribution<...>(...) function both of the classes above are non templates. The first class RandomEngine has zero function templates for its engines except the generalized getEngine() The difference with this class as opposed to the RandomDistribution is that every function in this class is a function template, because the <random> library's distribution functions require it. So now I have to not only template this generalized function as I did for getEngine() I have to also use a variadic parameter pack as different distributions takes a different amount of arguments.


Here is my declaration of my generalized function in the RandomDistribution class that is found in the header file:

template<typename Type, template<typename = Type> class Distribution, class... DistParams>
static Distribution<Type>& getDistribution( DistParams... params ) {
    return getUniformIntDistribution( params... );
}

Then I have this attempt of a specialization for this function in the cpp file for just one of the other distributions:

template<>
static std::uniform_real_distribution<>& RandomDistribution::getDistribution() {
    return RandomDistribution::getUniformRealDistribution();
}

I would like to do this for all the other distributions that I'm supporting.


I am using the stand alone function template randomGenerator() in my main function like this:

{
    std::initializer_list<std::size_t> list{};
    unsigned val = randomGenerator<std::mt19937, unsigned, std::uniform_int_distribution>
    ( RE::USE_CHRONO_CLOCK, std::size_t( 12 ), list, 1, 100 );
    std::cout << val << std::endl;

}

When I compile RandomGenerator.cpp file it compiles without error; however, when I compile main.cpp I am getting a compiler error stating that it can not convert from std::uniform_int_distribution<int>& to std::uniform_int_distribution<Type>&

and it is pointing to my class's generalized function template that is declared in RandomGenerator.hfile.

For some reason; Type is not being assigned or casted to the type that is passed into randomGenerator's template parameter list.

I'm stuck at this point. I know what the compiler message is saying; I don't what to do to fix. What can be done to resolve this conversion failure?

回答1:

Okay scrap that whole idea above: I went and entirely rewritten my classes into a single class. The class itself is now a class template. And it looks like this:

#ifndef GENERATOR_H
#define GENERATOR_H

#include <limits>
#include <chrono>
#include <random>
#include <type_traits>

enum SeedType { USE_CHRONO_CLOCK, USE_RANDOM_DEVICE, USE_SEED_VALUE, USE_SEED_SEQ };

template<class Engine, class Type, template<typename> class Distribution>
class Generator {
public:
    using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
        std::chrono::high_resolution_clock,
        std::chrono::steady_clock>;

private:
    Engine _engine;
    Distribution<Type> _distribution;
    Type _value;

public:

    template<class... Params>
    explicit Generator( Engine engine, Params... params ) : _engine( engine ) {
        _distribution = Distribution<Type>( params... );
    }

    void seed( SeedType type = USE_RANDOM_DEVICE, std::size_t seedValue = 0, std::initializer_list<std::size_t> list = {} ) {
        switch( type ) {
            case USE_CHRONO_CLOCK:  { _engine.seed( getTimeNow() );  break; }
            case USE_RANDOM_DEVICE: { std::random_device device{};
                                      _engine.seed( device() );      break; }
            case USE_SEED_VALUE:    { _engine.seed( seedValue );     break; }
            case USE_SEED_SEQ:      { std::seed_seq seq( list );
                                      _engine.seed( seq );           break; }
        }
    }

    void generate() {
        _value = _distribution( _engine );
    }

    Type getGeneratedValue() const {
        return _value;
    }

    Distribution<Type> getDistribution() const {
        return _distribution;
    }

    std::size_t getTimeNow() {
        std::size_t now = static_cast<std::size_t>(Clock::now().time_since_epoch().count());
        return now;
    }

};

#endif // !GENERATOR_H

And to use it is as simple as:

#include <iostream>
#include <iomanip>
#include <vector>
#include "generator.h"

int main() {

    // Engine, Seeding Type, & Distribution Combo 1
    std::mt19937 engine1;
    Generator<std::mt19937, short, std::uniform_int_distribution> g1( engine1, 1, 100 );
    g1.seed( USE_RANDOM_DEVICE );
    std::vector<short> vals1;
    for( unsigned int i = 0; i < 200; i++ ) {
        g1.generate();
        auto v = g1.getGeneratedValue();
        vals1.push_back( v );
    }

    int i = 0;
    for( auto& v : vals1 ) {

        if( (i % 10) != 0 ) {
            std::cout << std::setw( 3 ) << v << " ";
        } else {
            std::cout << '\n' << std::setw( 3 ) << v << " ";
        }       
        i++;
    }
    std::cout << "\n\n";

    // Engine, Seeding Type, & Distribution Combo 2
    std::ranlux48 engine2;
    std::initializer_list<std::size_t> list2{ 3, 7, 13, 17, 27, 31, 43 };

    Generator<std::ranlux48, unsigned, std::binomial_distribution> g2( engine2, 50, 0.75 );
    g2.seed( USE_SEED_SEQ, std::size_t(7), list2 );

    std::vector<unsigned> vals2;

    for( int i = 0; i < 200; i++ ) {
        g2.generate();
        auto v = g2.getGeneratedValue();
        vals2.push_back( v );
    }

    for( auto& v : vals2 ) {

        if( (i % 10) != 0 ) {
            std::cout << std::setw( 3 ) << v << " ";
        } else {
            std::cout << '\n' << std::setw( 3 ) << v << " ";
        }
        i++;
    }
    std::cout << "\n\n";

    // Engine, Seeding Type, & Distribution Combo 3
    std::minstd_rand engine3;
    Generator<std::minstd_rand, float, std::gamma_distribution> g3( engine3, 0.22222f, 0.7959753f );
    g3.seed( USE_CHRONO_CLOCK );

    std::vector<float> vals3;

    for( int i = 0; i < 200; i++ ) {
        g3.generate();
        auto v = g3.getGeneratedValue();
        vals3.push_back( v );
    }

    for( auto& v : vals3 ) {

        if( (i % 5 ) != 0 ) {
            std::cout << std::setw( 12 ) << v << " ";
        } else {
            std::cout << '\n' << std::setw( 12 ) << v << " ";
        }
        i++;
    }
    std::cout << "\n\n";


    std::cout << "\nPress any key and enter to quit.\n";
    std::cin.get();

    return 0;
}