C++ 如何使用(可变)模板进一步推广这一点
我在模板化代码的第二步或第二级时遇到问题。为了便于阅读,我将代码精简到了最基本的部分 我已经研究了很多模板问题,但我并没有真正解决我的确切问题 我现在有一个类记录,我是这样模板化的C++ 如何使用(可变)模板进一步推广这一点,c++,templates,variadic-templates,C++,Templates,Variadic Templates,我在模板化代码的第二步或第二级时遇到问题。为了便于阅读,我将代码精简到了最基本的部分 我已经研究了很多模板问题,但我并没有真正解决我的确切问题 我现在有一个类记录,我是这样模板化的 template <class T> class RIVRecord { private: std::vector<T> values; public: std::string name; RIVRecord(std::string _name, std::vec
template <class T>
class RIVRecord
{
private:
std::vector<T> values;
public:
std::string name;
RIVRecord(std::string _name, std::vector<T> _values) { name = _name; values = _values; };
~RIVRecord(void) { };
size_t size() {
return values.size();
}
T* Value(int index) {
return &values[index];
}
}
模板
课堂记录
{
私人:
std::向量值;
公众:
std::字符串名;
RIVRecord(std::string _name,std::vector _values){name=_name;values=_values;};
~RIVRecord(void){};
大小{
返回值。size();
}
T*值(整数索引){
返回值和值[索引];
}
}
很简单。T类型通常是基本类型,如浮点和整数。然后我想把这些记录放在一个DataSet类中。这是我遇到更多困难的地方。未经证实的情况是这样的:
class RIVDataSet
{
private :
//How to template this??
vector<RIVRecord<float>> float_records;
vector<RIVRecord<int>> int_records;
public:
RIVDataSet(void);
~RIVDataSet(void);
//And this
void AddRecord(RIVRecord<float> record) {
//How would this work?
}
//And this?
RIVRecord<float> GetFloatRecord();
};
类数据集
{
私人:
//如何模板这个??
矢量浮点数记录;
向量整数记录;
公众:
数据集(无效);
~z~数据集(无效);
//还有这个
作废添加记录(RIVCORD记录){
//这是怎么回事?
}
//这个呢?
RIVRecord GetFloatRecord();
};
我来自Java背景,因此在那里我可以使用向量
,并在询问记录时进行类型检查。但这在C++中似乎不可能。我尝试使用可变模板,但不确定如何使用模板中的所有类型构造向量:
template <class... Ts>
class RIVDataSet
{
private :
//For each T in Ts
vector<RIVRecord<T>> records;
public:
RIVDataSet(void);
~RIVDataSet(void);
//For each T in Ts
void AddRecord(RIVRecord<T> record) {
//How would this work?
}
//For each T in Ts, get the record by index.
RIVRecord<T> GetRecord(int index);
};
模板
类数据集
{
私人:
//对于Ts中的每个T
病媒记录;
公众:
数据集(无效);
~z~数据集(无效);
//对于Ts中的每个T
作废添加记录(RIVCORD记录){
//这是怎么回事?
}
//对于Ts中的每个T,按索引获取记录。
RIVRecord GetRecord(int索引);
};
我已经看到C++模板中的这种迭代是不可能的,但它只是为了澄清我想要的。
欢迎任何帮助,谢谢
编辑:
T的类型(浮点、整数等)数量没有限制
另外,GetRecord通过有一个索引来工作,但我并不太关心它,只要我可以迭代记录并获得正确的类型 您可以使用一种称为类型擦除的技术,但是您必须包含另一个级别的间接寻址。一些一般性反馈:
RIVRecord(std::string\u名称、std::vector\u值)
最好是:
RIVRecord(常量标准::字符串和\u名称,常量标准::向量和\u值)
为了避免不必要的复制,总的来说,对于大多数非原语的东西,经验法则是接受参数为const&
T*Value(int-index){return&values[index];}
是危险的,如果size()
超出向量
的容量()
,它将重新分配并使所有指针无效。在我看来,一个更好的接口应该是有一个T GetValue(
&void SetValue(tau值)
关于类型擦除,这就是RIVDataSet
的外观,我在这里使用一个名为Loki的库,如果你不想使用Loki,我会在后面给你一些提示
class RIVDataSet
{
private :
//How to template this??
struct HolderBase
{
virtual ~HolderBase() {}
};
template< typename T >
struct HolderImpl : HolderBase
{
// Use pointer to guarantee validity of returned record
std::vector< RIVRecord< T >* > m_Record;
};
typedef Loki::AssocVector< Loki::TypeInfo, HolderBase* > HolderMap;
HolderMap m_Records;
public:
~RIVDataSet()
{
for( HolderMap::iterator itrCur = m_Records.begin(); itrCur != m_Records.end(); ++itrCur ) delete itrCur->second;
}
//And this
template< typename T >
void AddRecord(const RIVRecord< T >& record )
{
HolderMap::iterator itrFnd = m_Records.find( typeid( T ) );
if( itrFnd == m_Records.end() )
itrFnd = m_Records.insert( std::make_pair( Loki::TypeInfo( typeid( T ) ), new HolderImpl< T >() ) ).first;
static_cast< HolderImpl< T >* >( itrFnd->second )->m_Record.push_back( new RIVRecord< T >( record ) );
}
template< typename T >
RIVRecord< T >* GetRecord()
{
HolderMap::iterator itrFnd = m_Records.find( typeid( T ) );
assert( itrFnd != m_Records.end() );
return itrFnd == m_Records.end() ? 0 : static_cast< HolderImpl< T >* >( itrFnd->second )->m_Record.front();
}
};
类数据集
{
私人:
//如何模板这个??
结构保持基
{
虚拟~HolderBase(){}
};
模板
结构HolderImpl:HolderBase
{
//使用指针保证返回记录的有效性
std::vector*>m_记录;
};
typedef-Loki::AssocVectorHolderMap;
持有MAP m_记录;
公众:
~RIVDataSet()
{
对于(HolderMap::iterator itrCur=m_Records.begin();itrCur!=m_Records.end();++itrCur)删除itrCur->second;
}
//还有这个
模板
void AddRecord(const-RIVRecord&记录)
{
迭代器itrFnd=m_Records.find(typeid(T));
if(itrFnd==m_Records.end())
itrFnd=m_记录。插入(std::make_对(Loki::TypeInfo(typeid(T)),新的HolderImpl())。首先;
静态播放(itrFnd->second)->m\u记录。向后推(新RIVRecord(记录));
}
模板
RIVRecord*GetRecord()
{
迭代器itrFnd=m_Records.find(typeid(T));
断言(itrFnd!=m_Records.end());
返回itrFnd==m_Records.end()?0:static_cast*>(itrFnd->second)->m_Record.front();
}
};
Loki::AssocVector
可以替代std::map
,但是您确实需要Loki::TypeInfo
,它只是std::type\u info
的包装。如果您查看一下Loki
中的代码,就可以很容易地自己实现一个。您不能使用可变模板来创建多个名称相同但类型不同的成员。事实上,你不可能有两个同名的成员。但是,您可以使用多重继承,并使用可变基类将成员放入基类中。然后可以在派生类中使用成员模板来解决歧义
下面的示例还使用完美转发来确保如果将临时文件传递给add()
,其资源可能会被“窃取”。你可以阅读更多关于这方面的内容
以下是一个例子:
#include <vector>
#include <utility>
// This templated base class holds the records for each type.
template <typename T>
class Base {
public:
// "T &&v" is a universal reference for perfect forwarding.
void add(T &&v) { records.push_back(std::forward<T>(v)); }
private:
std::vector<T> records;
};
// This inherits from Base<int>, Base<double>, for example, if you instantiate
// DataSet<int, double>.
template <typename... Ts>
class DataSet : public Base<Ts>... {
public:
// The purpose of this member template is to resolve ambiguity by specifying
// which base class's add() function we want to call. "U &&u" is a
// universal reference for perfect forwarding.
template <typename U>
void add(U &&u) {
Base<U>::add(std::forward<U>(u));
}
};
int main() {
DataSet<int, double> ds;
ds.add(1);
ds.add(3.14);
}
#包括
#包括
//此模板化基类保存每种类型的记录。
模板
阶级基础{
公众:
//“T&v”是完美转发的通用参考。
void add(T&&v){records.push_back(std::forward(v));}
私人:
病媒记录;
};
//这继承自Base,Base,例如,如果您实例化
//数据集。
模板
类数据集:公共基。。。{
公共图书馆
class BaseRIVRecord
{
};
template <class T>
class RIVRecord : public BaseRIVRecord
{
};
enum class RIVRecordsIndex
{
Float, Int
};
class RIVDataSet
{
public:
template<RIVRecordsIndex I, typename T>
void addRecord()
{
allmightyRecords.resize(I+1);
allmightyRecords[I].push_back(new RIVRecord<T>());
}
template<RIVRecordsIndex I, typename T>
RIVRecord<T>* get(unsigned int index)
{
return static_cast<RIVRecord<T>*>(allmighyRecords[I][index]);
}
private:
std::vector<std::vector<BaseRIVRecord*>> allmightyRecords;
};
int main()
{
RIVDataSet set;
set.addRecord<RIVRecordsIndex::Float, float>();
set.addRecord<RIVRecordsIndex::Float, float>();
set.addRecord<RIVRecordsIndex::Int, int>();
RIVRecord<int> r = set.get<RIVRecordsIndex::Int, int>(0);
}
template <typename... V>
class many_vectors
{
static_assert(are_all_different<V...>::value, "All types must be different!");
std::tuple<std::vector<V>...> _data;
public:
template<typename T>
std::vector<T>& data()
{ return std::get<index_of<T, V...>::value>(_data); }
template<typename T>
std::vector<T> const& data() const
{ return std::get<index_of<T, V...>::value>(_data); }
template<typename T>
void push_back(T&& arg)
{ data<typename std::remove_reference<T>::type>().push_back(std::forward<T>(arg)); }
template<typename T, typename... W>
void emplace_back(W&&... args)
{ data<T>().emplace_back(std::forward<W>(args)...); }
};
template<typename T, typename U, typename... V>
struct index_of : std::integral_constant<size_t, 1 + index_of<T, V...>::value>
{ };
template<typename T, typename... V>
struct index_of<T, T, V...> : std::integral_constant<size_t, 0>
{ };
template<typename T, typename... V>
struct is_different_than_all : std::integral_constant<bool, true>
{ };
template<typename T, typename U, typename... V>
struct is_different_than_all<T, U, V...>
: std::integral_constant<bool, !std::is_same<T, U>::value && is_different_than_all<T, V...>::value>
{ };
template<typename... V>
struct are_all_different : std::integral_constant<bool, true>
{ };
template<typename T, typename... V>
struct are_all_different<T, V...>
: std::integral_constant<bool, is_different_than_all<T, V...>::value && are_all_different<V...>::value>
{ };
v.push_back(int(3));
v.push_back<float>(4);
v.push_back<float>(5);
v.push_back(std::make_pair('a', 'b'));
v.emplace_back<std::pair<char, char>>('c', 'd');
std::cout << "ints:\n";
for(auto i : v.data<int>()) std::cout << i << "\n";
std::cout << "\n" "floats:\n";
for(auto i : v.data<float>()) std::cout << i << "\n";
std::cout << "\n" "char pairs:\n";
for(auto i : v.data<std::pair<char, char>>()) std::cout << i.first << i.second << "\n";
ints:
3
floats:
4
5
char pairs:
ab
cd