我有一个看起来像这样的课:Foo是我的班级; FooBar是一组来自库的不同类型的类,每个类都有独立的名称。
foo.h中
class Foo {
public:
Foo() = default;
// There many types, and many of these types have multiple constructors
// All of the appropriate overloads are available here.
template<class Type>
FooBar<Type>& getFooBarByFullName( ... ) {
// construct & return FooBar<Type>(...);
}
// Then I have a hand full of overloaded function template declarations
// with a generic name to call the appropriate functions from above.
// Each FooBar has different parameters, and FooBar is a class template.
template<class Type, template<typename> class FooBar>
FooBar<Type>& getFooBar(...);
};
// Outside of any class I have a generic function template
template<class Other, class Type, template<typename> class FooBar, class... FP>
Type doSomething( some param A, some param B, some param C, FP... params ) {
// Code here to work with Other using A, B & C
FooBar<Type> fooBar = getFooBar<Type, FooBar>( params... );
// do something to fooBar
return value generated from fooBar;
}
Foo.cpp中
#include **Foo.h**
template<class Type, template<typename> class FooBar>
FooBar<Type>& getFooBar(...) {
return {};
}
template<>
FooBar<int>& Foo::getFooBar( ... ) {
return getFooBarByFullName( ... );
}
template<>
FooBar<short>& Foo::getFooBar( ... ) {
return getFooBarByFullName( ... );
}
// etc...
我正在处理的其中一个模板参数的实现之一是class unary_op
。
我不想定义任何这样的类。我需要能够将函数对象,函数指针,lambda或std :: function作为unary_op
类传递给这些函数。
我遇到的问题是我的标题中的声明是否如下所示:
template<class IntType = int, class UnaryOp>
FooBar<IntType>& getFooBarByFullName( std::size_t count, double xmin, double xmax, UnaryOp fw ) {
// Constructors last parameter is defined as a class UnaryOp;
// but can be any of a function object, function pointer, lambda, std::function<...> etc.
FooBar<IntType> fooBar( count, xmin, xmax, fw );
}
// Then I can declare the appropriate generic declaration overload here
template<class Type, template<typename> class FooBar, class FuncOp>
FooBar<Type>& getFooBar( std::size_t count, double xmin, double xmax, FuncOp fw ); // Declaration only
然而,当我去cpp文件中使用提供的适当重载声明来编写定义显式特化时,同时试图避免歧义是我遇到麻烦的地方。
template<>
FooBar<int>& Foo::getFooBar( std::size_t count, double xmin, double xmax, ? ) {
return getFooBarByFullName<int>( count, xmin, xmax, ? );
}
template<>
FooBar<short>& Foo:getFooBar( std::size_t count, double xmin, double xmax, ? ) {
return getFooBarByFullName<short>( count, xmin, xmax, ? );
}
如您所见,我不知道如何定义class UnaryOp
类型的最后一个参数。我还希望能够支持调用者可以传递我上面提到的任何类型:function object
,function pointer
,lambda
,std::function<>
作为最后一个参数UnaryOp
。我不知道从哪里开始......
编辑 - 我忘记在我的实际代码中提到它;上面的两个类删除了默认构造函数;并且所有类方法都是静态的。
答案 0 :(得分:1)
目前还不清楚你究竟在问什么,但看起来你的问题是在你的.cpp文件中创建一个可实例化但通用的函数。我认为有两种方法可以解决这个问题:
放弃您的计划:使这些方法模板仅生成.hpp文件并将UnaryOp
作为(可推导)模板参数。
.hpp:
template<typename Type, typename UnaryOp>
Type Qoo(Type const&x, UnaryOp&&func)
{
// some simple/short code calling func()
}
在.cpp文件中为UnaryOp
= std::function
实现函数重载,并在您的.cpp中将常规UnaryOp
(lambda,functor,函数指针等)实现为模板。 hpp文件,使用从std::function
创建的UnaryOp
对象调用前者。
.hpp:
template<typename Type>
Type Qoo(Type const&, std::function<Type(Type)>&&);
template<typename Type, typename UnaryOp>
Type Qoo(Type const&x, UnaryOp&&func)
{
return Qoo(x, std::function<Type(Type)>{func});
}
.cpp
template<typename Type>
Type Qoo(Type const&t, std::function<Type(Type)>&&func);
{
// some lengthy code calling func()
}
// explicit instantiations
template int Qoo(int const&, std::function<int(int)>&&);
template short Qoo(short const&, std::function<short(short)>&&);
...
第二个版本允许预编译,但在UnaryOp
≠std::function<>
的情况下会产生开销。
第一种解决方案避免了这种开销,但将完整的实现暴露给.hpp文件,并没有提供预编译的好处。
在类似情况下,如果实现的代码很大,我倾向于使用第二个版本,这样可以容忍std::function
对象的开销,而第一个版本只适用于小代码,通常应该是无论如何inline
。
最后,请注意,在.cpp文件中,您不需要定义所有特化,而是提供模板并指定显式实例化。
答案 1 :(得分:0)
好吧废弃上面的整个想法:我把我的课程完全重写为一个单独的课程。该类本身现在是一个类模板。它看起来像这样:
#ifndef GENERATOR_H
#define GENERATOR_H
#include <limits>
#include <chrono>
#include <random>
#include <type_traits>
enum SeedType { USE_CHRONO_CLOCK, USE_RANDOM_DEVICE, USE_SEED_VALUE, USE_SEED_SEQ };
template<class Engine, class Type, template<typename> class Distribution>
class Generator {
public:
using Clock = std::conditional_t<std::chrono::high_resolution_clock::is_steady,
std::chrono::high_resolution_clock,
std::chrono::steady_clock>;
private:
Engine _engine;
Distribution<Type> _distribution;
Type _value;
public:
template<class... Params>
explicit Generator( Engine engine, Params... params ) : _engine( engine ) {
_distribution = Distribution<Type>( params... );
}
void seed( SeedType type = USE_RANDOM_DEVICE, std::size_t seedValue = 0, std::initializer_list<std::size_t> list = {} ) {
switch( type ) {
case USE_CHRONO_CLOCK: { _engine.seed( getTimeNow() ); break; }
case USE_RANDOM_DEVICE: { std::random_device device{};
_engine.seed( device() ); break; }
case USE_SEED_VALUE: { _engine.seed( seedValue ); break; }
case USE_SEED_SEQ: { std::seed_seq seq( list );
_engine.seed( seq ); break; }
}
}
void generate() {
_value = _distribution( _engine );
}
Type getGeneratedValue() const {
return _value;
}
Distribution<Type> getDistribution() const {
return _distribution;
}
std::size_t getTimeNow() {
std::size_t now = static_cast<std::size_t>(Clock::now().time_since_epoch().count());
return now;
}
};
#endif // !GENERATOR_H
使用它就像:
#include <iostream>
#include <iomanip>
#include <vector>
#include "generator.h"
int main() {
// Engine, Seeding Type, & Distribution Combo 1
std::mt19937 engine1;
Generator<std::mt19937, short, std::uniform_int_distribution> g1( engine1, 1, 100 );
g1.seed( USE_RANDOM_DEVICE );
std::vector<short> vals1;
for( unsigned int i = 0; i < 200; i++ ) {
g1.generate();
auto v = g1.getGeneratedValue();
vals1.push_back( v );
}
int i = 0;
for( auto& v : vals1 ) {
if( (i % 10) != 0 ) {
std::cout << std::setw( 3 ) << v << " ";
} else {
std::cout << '\n' << std::setw( 3 ) << v << " ";
}
i++;
}
std::cout << "\n\n";
// Engine, Seeding Type, & Distribution Combo 2
std::ranlux48 engine2;
std::initializer_list<std::size_t> list2{ 3, 7, 13, 17, 27, 31, 43 };
Generator<std::ranlux48, unsigned, std::binomial_distribution> g2( engine2, 50, 0.75 );
g2.seed( USE_SEED_SEQ, std::size_t(7), list2 );
std::vector<unsigned> vals2;
for( int i = 0; i < 200; i++ ) {
g2.generate();
auto v = g2.getGeneratedValue();
vals2.push_back( v );
}
for( auto& v : vals2 ) {
if( (i % 10) != 0 ) {
std::cout << std::setw( 3 ) << v << " ";
} else {
std::cout << '\n' << std::setw( 3 ) << v << " ";
}
i++;
}
std::cout << "\n\n";
// Engine, Seeding Type, & Distribution Combo 3
std::minstd_rand engine3;
Generator<std::minstd_rand, float, std::gamma_distribution> g3( engine3, 0.22222f, 0.7959753f );
g3.seed( USE_CHRONO_CLOCK );
std::vector<float> vals3;
for( int i = 0; i < 200; i++ ) {
g3.generate();
auto v = g3.getGeneratedValue();
vals3.push_back( v );
}
for( auto& v : vals3 ) {
if( (i % 5 ) != 0 ) {
std::cout << std::setw( 12 ) << v << " ";
} else {
std::cout << '\n' << std::setw( 12 ) << v << " ";
}
i++;
}
std::cout << "\n\n";
std::cout << "\nPress any key and enter to quit.\n";
std::cin.get();
return 0;
}