Class member function template partial specialization with variadic parameters [closed]

限于喜欢 提交于 2019-12-24 10:27:37

问题


I'm using Visual Studio 2017 CE version 15.6.2 with compiler language options set to:

ISO C++ Latest Draft Standard (/std:c++latest)


I'm working with a majority of the functions from <random> and I have 2 classes that are non template classes, RandomEngine and RandomDistribution.

These classes can not be constructed as they have deleted default constructors. All the methods are static within the classes.


Within the RandomEngine class my static methods are named according to some of the random engines in the standard library such as std::mt19937. It supports the functionality to seed the engines by different mechanisms depending on the enumeration type passed into the static function and other parameters that are needed. There are 4 ways that any of these engines can be seeded: {CHRONO_CLOCK, SEED_VALUE, SEED_SEQ, RANDOM_DEVICE }. There is one function template that has a generic name getEngine(...) and using specialization I was able to create this function returning each of the different types of engines. The class is too big but I will show a couple of examples:


RandomGenerator.h

#ifndef RANDOM_GENERATOR_H
#define RANDOM_GENERATOR_H

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

class RandomEngine {
public:
    enum SeedType { USE_CHRONO_CLOCK, USE_RANDOM_DEVICE, USE_SEED_VALUE, USE_SEED_SEQ };

    using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
               std::chrono::high_resolution_clock,
               std::chrono::steady_clock>;


    RandomEngine() = delete;

protected:
    static std::random_device& getRandomDevice() {
        static std::random_device device{};
        return device;
    }

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

    static std::seed_seq& getSeedSeq( std::initializer_list<std::size_t>& list ) {
        static std::seed_seq seq( list );
        return seq;
    }

public:
    // I'll just show two to keep the list short; but they all follow the same pattern.
    static std::default_random_engine& getDefaultRandomEngine( SeedType type, std::size_t seedVal, std::initializer_list<std::size_t> list  ) {
        static std::default_random_engine engine{};
        switch( type ) {
            case USE_CHRONO_CLOCK: { engine.seed( getTimeNow() ); break; } 
            case USE_SEED_VALUE: { engine.seed( seedVal  ); break; }
            case USE_SEED_SEQ: { engine.seed( getSeedSeq( list ) ); break; }
            default:{engine.seed( getRandomDevice()() ); break; }
        }
        return engine;
    }

    static std::mt19937& getMt19937( SeedType type, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
        static std::mt19937 engine{};
        switch( type ) {
            case USE_CHRONO_CLOCK: { engine.seed( getTimeNow() ); break; }
            case USE_SEED_VALUE: { engine.seed( seedValue ); break; }
            case USE_SEED_SEQ: { engine.seed( getSeedSeq( list ) ); break; }
            default: { engine.seed( getRandomDevice()() ); break; }
        }
        return engine;
    }

    // After the rest of the engine types about 8-10 more...
    // I have this function template within the above class.
    template<class Engine>
    static Engine& getEngine( RandomEngine::SeedType seedType, std::size_t seedValue, std::initializer_list list ) {
        return getDefaultRandomEngine( seedType, seedValue, list );
    }
}; 


// ... other class here but will get to that in a bit.
class RandomDistribution { ... };

typedef RandomEngine RE;
typedef RandomDistribution RD;

// function template here which I will get to in a bit.

#endif // !RANDOM_GENERATOR_H

Then in my RandomGenerator.cpp file I specialized the RandomEngine::getEngine(...) function as such:

RandomGenerator.cpp

 #include "RandomGenerator.h"

// specializations of different engines
template<>
static std::knuth_b& RandomEngine::getEngine( SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
    return getKnuthB( seedType, seedValue, list );
}

template<>
static std::minstd_rand0& RandomEngine::getEngine( SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
    return getMinStd_Rand0( seedType, seedValue, list );
}

template<>
static std::minstd_rand& RandomEngine::getEngine( SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
    return getMinStd_Rand( seedType, seedValue, list );
}

template<>
static std::mt19937& RandomEngine::getEngine( SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
    return getMt19937( seedType, seedValue, list );
}

template<>
static std::mt19937_64& RandomEngine::getEngine( SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
    return getMt19937_64( seedType, seedValue, list );
}

template<>
static std::ranlux24& RandomEngine::getEngine( SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
    return getRanLux24( seedType, seedValue, list );
}

template<>
static std::ranlux24_base& RandomEngine::getEngine( SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
    return getRanLux24_base( seedType, seedValue, list );
}

template<>
static std::ranlux48& RandomEngine::getEngine( SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
    return getRanLux48( seedType, seedValue, list );
}

template<>
static std::ranlux48_base& RandomEngine::getEngine( SeedType seedType, std::size_t seedValue, std::initializer_list<std::size_t> list ) {
    return getRanLux48_base( seedType, seedValue, list );
}

These specializations above all seem to compile and work correctly. It is when I begin to follow a similar pattern for my RandomDistribution class members that I begin to get into trouble.


The list to these is a lot longer than the engines above but every function in this class is a static function template as different distributions take different types and they have different parameters for their constructors.

This class is in the same header file above and it looks like this and I'll limit it to a few examples:

class RandomDistribution {
public:
    RandomDistriubtion() = delete;

// UNIFORM DISTRIBUTIONS
template<class IntType = int>
static std::uniform_int_distribution<IntType>& getUniformIntDistribution( IntType lowerBound = 0, IntType upperBound = (std::numeric_limits<IntType>::max)() ) {
    static std::uniform_int_distribution<IntType> dist( lowerBound, upperBound );
    return dist;
}

template<class RealType = double>
static std::uniform_real_distribution<RealType>& getUniformRealDistribution( RealType lowerBound = 0.0, RealType upperBound = 1.0 ) {
    static std::uniform_real_distribution<RealType> dist( lowerBound, upperBound );
    return dist;
}

[...] // More distributions here    

template<class RealType = double, class InputIt1, class InputIt2>
static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution( InputIt1 first_i, InputIt1 last_i, InputIt2 first_w ) {
    static std::piecewise_linear_distribution<RealType> dist( first_i, last_i, first_w );
    return dist;
}

template<class RealType = double, class UnaryOperation>
static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution( std::initializer_list<RealType> bl, UnaryOperation fw ) {
    static std::piecewise_linear_distribution<RealType> dist( bl, fw );
    return dist;
}

template<class RealType = double, class UnaryOperation>
static std::piecewise_linear_distribution<RealType>& getPiecewiseLinearDistribution( std::size_t nw, RealType xmin, RealType xmax, UnaryOperation fw ) {
    static std::piecewise_linear_distribution<RealType> dist( nw, xmin, xmax, fw );
    return dist;
}

    // function template with variadic pamater for specialization.
    getDistribution()  ...  see below
};

As you can see from the class above with the long list of distributions; all of these static methods work as is; but I would like to do the same thing as I did with the RandomEngine functions. I want to create a function template then specialize each of these. The only thing is because some of these take different types such as Real & Int and some take 2 parameters while others can take 3 or more; I need to use variadic templates for this function.

In the public section of the RandomDistriubtion class above I have this declaration/definition attempt.

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

My first attempt of writing the specializations in the cpp file is shown here:

// specializations of different distributions
template<>
static std::uniform_real_distribution<>& RandomDistribution::getDistribution() {
    return RandomDistribution::getUniformRealDistribution();
}

Besides the specializations I also have this stand alone function template that is declared-defined at the bottom of the header file after the two typedefs:

// Made some edits to this function template; I changed the template
// paramater for `Type` from `class` to `typename` and I changed the
// local variable declarations to have static storage instead.
// I also added a forgotten return to `Type retVal`
// I also fixed the call to `getDistribution` by changing its 
// template parameter list to `<Type, Distribution>` as suggested 
// by user max66 which allowed me to move further ahead.
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<Distribution<Type>>( params... );
    retVal = dist( engine );
    return retVal;
 }

I'm trying to call the generalized functions from the 2 classes above within this function template. Basically I'm trying to streamline the process into a single function call to generate any type of random distribution using any of the provided engines that can be seeded by any of the seeding types and it will generate and return a random value of type T.


This is how it looks in main.

#include "RandomGenerator.h"

int main() {
    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 );

    return 0;
}

When I compile RandomGenerator.cpp it compiles without error, but when I compile main.cpp I'm getting these compiler errors:

1>------ Build started: Project: ChemLab, Configuration: Debug Win32 ------
1>main.cpp
1>c:\...\visual studio 2017\projects\chemlab\engine\randomgenerator.h(599): error C2672: 'linx::RandomDistribution::getDistribution': no matching overloaded function found
1>c:\...\visual studio 2017\projects\chemlab\chemlab\main.cpp(13): note: see reference to function template instantiation 'Type linx::randomGenerator<std::mt19937,unsigned int,std::uniform_int_distribution,int,int>(linx::RandomEngine::SeedType,::size_t,std::initializer_list<_Ty>,int,int)' being compiled
1>        with
1>        [
1>            Type=unsigned int,
1>            _Ty=std::seed_seq::result_type
1>        ]
1>c:\...\visual studio 2017\projects\chemlab\engine\randomgenerator.h(596): error C2783: 'Distribution<Type> &linx::RandomDistribution::getDistribution(DistParams...)': could not deduce template argument for 'Distribution'
1>c:\...\visual studio 2017\projects\chemlab\engine\randomgenerator.h(577): note: see declaration of 'linx::RandomDistribution::getDistribution'
1>Done building project "ChemLab.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

I know what the errors mean the first being no matching overload, and the second could not deduce template argument for Distribution.

I just don't know what is actually causing them and how to resolve these compiler errors. Is it the template template parameter? Is it with the underlying type? Is it with the variadic parameter pack? If I can get some of the specializations working; I should be able to get the rest.

I know that this is a lot to take in but I do truly appreciate those who have taken the time to read and look through this. Any and all thoughts are welcome.


EDIT - I have brought this class library over from an older version of Visual Studio VS2015 but the original project might of been written in 2010, 2012 or 2013 version... From the RandomEngine class I had to remove the getSeedSeq function because in 2017 the copy constructor & move constructors are deleted functions. You can ignore that part of the class for setting up the engines. I instead created an instance of a seed_seq in the appropriate case statements and I passed the initializer_list to its constructor. Then I passed that static instance into the engine.seed() function. Just something to be aware of. And it did not change the compiler errors they are still the same after this fix.

Edit Okay I made some corrects to the function template randomGenerator() as suggested by user max66; you can see that in the code section above.

Now that I have those fixes; I'm getting a slightly different compiler error.

error C2440: 'return': cannot convert from 'std::uniform_int_distribution<int>' to 'std::uniform_int_distribution<Type> &'

So now it's not able to convert from uniform_int_distriubtion<int>& to uniform_int_distribution<Type>&. So now I'm trying to figure out how to get the conversions correct.


回答1:


We have to work with the concept of "minimal, complete and verifiable example" and I see a lot of problems in your code (please: next time prepare a code that can compile) but the problem that visual studio show start from getDistribution() definition

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

so a method that require a template type (Type), a template-template template parameter (Distribution) and other template types (DistParams...) matching the (deductibles from) methods arguments (params...).

But your getDistribution() call in RandomGenerator() is the following

dist = RD::getDistribution<Distribution<Type>>( params... );

So you explicit, as template parameter, a type (Distribution<Type>) not a type and a template-template parameter.

I suppose your intention was to call getDistribution() as follows

//             first Type  vvvv  vvvvvvvvvvvv  Distribution next
dist = RD::getDistribution<Type, Distribution>( params... );


来源:https://stackoverflow.com/questions/49336082/class-member-function-template-partial-specialization-with-variadic-parameters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!