具有可变参数

时间:2018-03-17 12:35:20

标签: c++ c++17 variadic-templates template-specialization partial-specialization

我使用Visual Studio 2017 CE版本15.6.2,编译器语言选项设置为:

  

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

我正在处理<random>中的大部分功能,我有2个非模板类,RandomEngineRandomDistribution

无法构造这些类,因为它们已删除默认构造函数。所有方法在类中都是静态的。

RandomEngine类中,我的静态方法是根据标准库中的一些随机引擎命名的,例如std::mt19937。它支持通过不同机制为引擎设定种子的功能,具体取决于传递给静态函数的枚举类型和所需的其他参数。可以通过4种方式为这些引擎添加任何种子:{CHRONO_CLOCKSEED_VALUESEED_SEQRANDOM_DEVICE}。有一个函数模板具有通用名称getEngine(...)并使用特化我能够创建此函数返回每个不同类型的引擎。这堂课太大了,但我会举几个例子:

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

然后在我的RandomGenerator.cpp文件中,我将RandomEngine::getEngine(...)函数专门化为:

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 );
}

这些专业化似乎可以编译并正常工作。当我开始对我的RandomDistribution班级成员采用类似的模式时,我开始遇到麻烦。

这些列表比上面的引擎要长很多,但是这个类中的每个函数都是一个静态函数模板,因为不同的发行版采用不同的类型,它们的构造函数有不同的参数。

这个类在上面的相同头文件中,它看起来像这样,我将它限制为几个例子:

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
};

正如您从上面的课程中可以看到的那样,有很长的分布列表;所有这些静态方法都按原样工作;但我想做与RandomEngine函数相同的事情。我想创建一个函数模板,然后专门化这些模板。唯一的原因是因为其中一些采用了不同的类型,例如Real&amp; Int有些参数需要2个参数,而其他参数可能需要3个或更多;我需要为这个函数使用可变参数模板。

在上面RandomDistriubtion类的公共部分,我有这个声明/定义尝试。

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

我在cpp文件中首次尝试编写专业文字如下所示:

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

除了特化之外,我还有这个独立的函数模板,在两个typedef之后在头文件的底部声明定义:

// 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;
 }

我尝试在此函数模板中调用上面2个类的通用函数。基本上我尝试将流程简化为单个函数调用,以使用任何提供的引擎生成任何类型的随机分布,这些引擎可以由任何种子类型播种,并且它将生成并返回随机值{ {1}}。

这就是它在主要方面的表现。

type T

当我编译RandomGenerator.cpp时,它编译时没有错误,但是当我编译main.cpp时,我得到了这些编译器错误:

#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;
}

我知道错误意味着第一个没有匹配的重载,第二个错误不能推导出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 ========== 的模板参数。

我只是不知道实际导致它们的原因以及如何解决这些编译器错误。它是模板模板参数吗?是基础类型吗?它是可变参数包吗?如果我可以让一些专业工作;我应该能够得到其余的。

我知道这需要考虑很多,但我确实非常感谢那些花时间阅读并仔细研究过的人。欢迎任何和所有的想法。

编辑 - 我从旧版本的Visual Studio VS2015中引入了这个类库,但原始项目可能是在2010年,2012年或2013年版本中编写的......来自{{1我必须删除Distribution函数,因为在2017年复制构造函数&amp;移动构造函数是删除的函数。您可以忽略该类的这一部分来设置引擎。我在相应的case语句中创建了一个RandomEngine的实例,并将getSeedSeq传递给它的构造函数。然后我将该静态实例传递给seed_seq函数。只是需要注意的事情。并且它没有改变编译器错误,在修复后它们仍然是相同的。

编辑好的,我按照用户max66的建议对功能模板initializer_list进行了一些更正;你可以在上面的代码部分看到。

现在我有了这些修复;我的编译器错误略有不同。

engine.seed()

现在它无法从randomGenerator()转换为error C2440: 'return': cannot convert from 'std::uniform_int_distribution<int>' to 'std::uniform_int_distribution<Type> &' 。所以现在我想弄清楚如何让转换正确。

1 个答案:

答案 0 :(得分:1)

我们必须使用&#34; 最小的概念,完整且可验证的示例&#34;我在你的代码中看到很多问题(请:下次准备一个可以编译的代码)但是visual studio show从getDistribution()定义开始的问题

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

所以需要模板类型(Type),模板模板模板参数(Distribution)和其他模板类型(DistParams...)匹配(免除)方法的方法参数(params...)。

getDistribution()中的RandomGenerator()来电是以下

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

因此,您明确指定类型(Distribution<Type>)作为模板参数,而不是类型和模板模板参数。

我想您的意图是按照以下方式致电getDistribution()

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