【发布时间】:2018-11-21 22:09:08
【问题描述】:
我正在使用 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 和 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 个类中的通用函数。基本上,我试图将过程简化为单个函数调用,以使用任何提供的引擎生成任何类型的随机分布,这些引擎可以由任何播种类型播种,它将生成并返回随机值type T .
这是它在 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;
}
当我编译 RandomGenerator.cpp 时,它编译时没有错误,但是当我编译 main.cpp 时,我得到了这些编译器错误:
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 ==========
我知道错误意味着第一个错误是没有匹配的重载,第二个错误无法推断出Distribution 的模板参数。
我只是不知道究竟是什么导致了它们以及如何解决这些编译器错误。它是模板模板参数吗?它与基础类型有关吗?是否带有可变参数包?如果我能让一些专业化工作;我应该能够得到其余的。
我知道这有很多内容需要吸收,但我真的很感谢那些花时间阅读和浏览本文的人。欢迎提出任何想法。
编辑 - 我已经从旧版本的 Visual Studio VS2015 中引入了这个类库,但原始项目可能是在 2010、2012 或 2013 版本中编写的......来自RandomEngine类我不得不删除 getSeedSeq 函数,因为在 2017 年,复制构造函数和移动构造函数是已删除的函数。您可以忽略课程中设置引擎的那一部分。相反,我在适当的 case 语句中创建了一个 seed_seq 的实例,并将 initializer_list 传递给它的构造函数。然后我将该静态实例传递给engine.seed() 函数。只是需要注意的事情。而且它并没有改变编译器错误,修复后它们仍然相同。
编辑好的,我按照用户 max66 的建议对函数模板 randomGenerator() 进行了一些更正;您可以在上面的代码部分中看到这一点。
现在我有了这些修复;我得到一个稍微不同的编译器错误。
error C2440: 'return': cannot convert from 'std::uniform_int_distribution<int>' to 'std::uniform_int_distribution<Type> &'
所以现在它无法从uniform_int_distriubtion<int>& 转换为uniform_int_distribution<Type>&。所以现在我想弄清楚如何正确地进行转换。
【问题讨论】:
-
请提供minimal reproducible example。什么是 RandomGenerator.h?另外,这是很多代码,所以请考虑mcve的m初始部分。
-
@Barry 这是一个错字,实际文件是正确的。我修正了错字。我试图加入最少的必要量。不必通读类的每一个函数,只需浏览其声明的设计模式即可。问题 - 问题在于帖子的底部,该部分严格处理 RandomDistribution 类,同时尝试在使用可变参数模板参数时实现部分特化或仅特化。
-
将
return getUniformIntDistribution(params...);更改为return getUniformIntDistribution<Type>(params...);。否则它将使用默认模板参数,即int而不是unsigned。 -
我想回答我原来的问题,因为我已经重写了它,但它现在关闭了;所以我可以轻松做的就是将链接指向我对这个问题的答案:stackoverflow.com/a/49352138/1757805 否则我也会在这里发布。
标签: c++ c++17 variadic-templates template-specialization partial-specialization