我有一个带有重置方法的模板化数组类。我想优化重置代码以在POD上使用memset,并在非POD上调用用户提供的reset()。
我想要(1)方法(重置)的相同名称,并根据模板参数使用相关实现,以及(2)如果使用非pod类型T而没有reset()函数,则编译时错误
template <typename T>
class CArray {
private:
T* array;
size_t size;
public:
void reset() {
memset(array, 0, size);
}
void reset() {
for (size_t i=0; i<size; ++i)
array[i].reset();
}
}
CArray<int> arr1;
arr1.reset(); -> will use memset and be fast
struct t2 { int* mem; void reset() {} };
CArray<t2> arr2;
arr2.reset(); -> will call t2.reset for each element
struct t3 { int* mem; };
CArray<t3> arr3;
arr3.reset(); -> should fail compilation since t3.reset() is missing
我尝试过使用type_traits,但我不确定这是否正确(可以帮助编译时检查)吗?
答案 0 :(得分:2)
template <typename T>
class CArray {
private:
T* array;
size_t size;
public:
void reset() {
CArrayReset( this );
}
void MemberReset()
{
for (size_t i=0; i<size; ++i)
array[i].reset();
}
void MemsetReset()
{
memset(array, 0, size);
}
};
template <typename T>
typename disable_if<is_pod<T>, void>::type
CArrayReset( CArray<T>* arr )
{
arr->MemberReset();
}
template <typename T>
typename enable_if<is_pod<T>, void>::type
CArrayReset( CArray<T>* arr )
{
arr->MemsetReset();
}
void tryitout()
{
CArray<int> arr1;
arr1.reset(); // will use memset and be fast
struct t2 { int* mem; void reset() {} };
CArray<t2> arr2;
arr2.reset(); // will call t2.reset for each element
struct t3 { virtual void foo(){} int* mem; }; // added virtual func to make it non pod
CArray<t3> arr3;
arr3.reset(); // will not compile
}
(省略各种标题)
答案 1 :(得分:1)
试图在POD上强制memset
似乎是一个毫无意义的“优化”IMO
#include <array>
#include <cstring>
struct bar
{
int i;
int j;
};
std::array<bar, 4> test;
void foo()
{
memset(&test, 0, sizeof test);
}
void foo2()
{
test.fill(bar());
}
使用-O3 -c -S
进行编译可以得到:
__Z3foov:
LFB944:
movq $0, _test(%rip)
movq $0, 8+_test(%rip)
movq $0, 16+_test(%rip)
movq $0, 24+_test(%rip)
ret
...
__Z4foo2v:
LFB945:
pxor %xmm0, %xmm0
movaps %xmm0, _test(%rip)
movaps %xmm0, 16+_test(%rip)
ret
专注于编写易于理解的代码,让编译器负责优化。只要您的类具有默认构造函数,您就不需要使用SFINAE。
对于您的课程,reset
方法可以按以下方式工作
void reset() {
std::fill_n(array, size, T());
}
答案 2 :(得分:1)
不能在类模板的非模板成员函数上使用SFINAE,因为这将部分地特化一个函数,这是不允许的(感谢{{3}的dyp })。
然而,您可以非常轻松地使用标签调度,正如该文章所暗示的那样。
演示代码。请注意,我已将阵列更改为使用std::vector
进行内存管理,以使我的演示更简单(实际上还不清楚为什么您的阵列不会这样做!)
#include <iostream>
#include <vector>
#include <string>
#include <type_traits>
struct Baz
{
void reset() { std::cout << "Baz reset" << std::endl; }
Baz(int x = 1) { }
};
template <typename T>
struct CArray {
std::vector<T> ptr;
void reset()
{
do_reset( std::is_pod<T>{} );
}
private:
void do_reset(std::true_type) { std::fill(ptr.begin(), ptr.end(), T()); }
void do_reset(std::false_type) { for (auto &x : ptr) x.reset(); }
};
int main()
{
CArray<int> x;
x.ptr.push_back(1);
x.reset(); // OK, no output
CArray<std::string> y;
// y.reset(); // compilation error - std::string has no member "reset"
CArray<Baz> z;
z.ptr.push_back( Baz() );
z.reset(); // OK, output "Baz reset"
}