【问题标题】:Class member function template partial specialization with variadic parameters [closed]具有可变参数的类成员函数模板部分特化[关闭]
【发布时间】:2018-11-21 22:09:08
【问题描述】:

我正在使用 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 函数相同的事情。我想创建一个函数模板,然后专门化其中的每一个。唯一的问题是,其中一些采用不同的类型,例如 RealInt,有些采用 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&lt;int&gt;&amp; 转换为uniform_int_distribution&lt;Type&gt;&amp;。所以现在我想弄清楚如何正确地进行转换。

【问题讨论】:

  • 请提供minimal reproducible example。什么是 RandomGenerator.h?另外,这是很多代码,所以请考虑mcve的m初始部分。
  • @Barry 这是一个错字,实际文件是正确的。我修正了错字。我试图加入最少的必要量。不必通读类的每一个函数,只需浏览其声明的设计模式即可。问题 - 问题在于帖子的底部,该部分严格处理 RandomDistribution 类,同时尝试在使用可变参数模板参数时实现部分特化或仅特化。
  • return getUniformIntDistribution(params...); 更改为 return getUniformIntDistribution&lt;Type&gt;(params...);。否则它将使用默认模板参数,即int 而不是unsigned
  • 我想回答我原来的问题,因为我已经重写了它,但它现在关闭了;所以我可以轻松做的就是将链接指向我对这个问题的答案:stackoverflow.com/a/49352138/1757805 否则我也会在这里发布。

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


【解决方案1】:

我们必须使用“minimal,完整且可验证的示例”的概念,我在您的代码中看到了很多问题(请:下次准备一个可以编译的代码)但是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...) 的方法.

但是您在RandomGenerator() 中的getDistribution() 调用如下

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

因此,作为模板参数,您显式使用类型 (Distribution&lt;Type&gt;) 而不是类型和模板模板参数。

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

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

【讨论】:

  • 这对我有一点帮助;现在我可能还可以处理其他错误,但我也犯了另一个小错误。在我的 randomGenerator() 函数中,我忘记了实际返回值。
  • 我对原来的帖子做了一些修改;我按照您的建议修复了该功能,但我还进行了一些其他调整;您可以在上面的源代码中阅读它们。但是,它现在正在生成一个不同的编译器错误,您可以在问题的底部阅读该错误。它不能从uniform_int_distribution&lt;int&gt;&amp; 转换为uniform_int_distribution&lt;Type&gt;&amp;。有什么建议么?如果没有,我可以问一个单独的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-03
  • 1970-01-01
  • 2012-12-15
相关资源
最近更新 更多