我使用Visual Studio 2017 CE版本15.6.2,编译器语言选项设置为:
ISO C++ Latest Draft Standard (/std:c++latest)
我正在处理<random>
中的大部分功能,我有2个非模板类,RandomEngine
和RandomDistribution
。
无法构造这些类,因为它们已删除默认构造函数。所有方法在类中都是静态的。
在RandomEngine
类中,我的静态方法是根据标准库中的一些随机引擎命名的,例如std::mt19937
。它支持通过不同机制为引擎设定种子的功能,具体取决于传递给静态函数的枚举类型和所需的其他参数。可以通过4种方式为这些引擎添加任何种子:{CHRONO_CLOCK
,SEED_VALUE
,SEED_SEQ
,RANDOM_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> &'
。所以现在我想弄清楚如何让转换正确。
答案 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... );