C++ 快速模板阵列重置方法的实现
我有一个带有重置方法的模板化数组类。我想优化重置代码,在POD上使用memset,在非POD上调用用户提供的重置 我想要1个相同的名称用于方法reset,并根据模板参数使用相关的实现,如果使用非pod类型T而没有reset函数,则需要2个编译时错误C++ 快速模板阵列重置方法的实现,c++,templates,metaprogramming,typetraits,C++,Templates,Metaprogramming,Typetraits,我有一个带有重置方法的模板化数组类。我想优化重置代码,在POD上使用memset,在非POD上调用用户提供的重置 我想要1个相同的名称用于方法reset,并根据模板参数使用相关的实现,如果使用非pod类型T而没有reset函数,则需要2个编译时错误 template <typename T> class CArray { private: T* array; size_t size; public: void reset() { memset(
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,但我不确定这是否是正确的方向,它是否有助于编译时检查?尝试在pod上强制使用memset似乎是一种毫无意义的优化
#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());
}
只需专注于编写易于理解的代码,并让编译器负责优化。只要类有一个默认构造函数,就不需要使用SFINAE
对于您的类,reset方法可以按如下方式工作
void reset() {
std::fill_n(array, size, T());
}
在我看来,试图在pod上强制使用memset似乎是一种毫无意义的优化
#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());
}
只需专注于编写易于理解的代码,并让编译器负责优化。只要类有一个默认构造函数,就不需要使用SFINAE
对于您的类,reset方法可以按如下方式工作
void reset() {
std::fill_n(array, size, T());
}
省略了各种标题
省略了各种标题不可能在类模板的非模板成员函数上使用SFINAE,因为这将部分地专门化函数,这是由于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"
}
在类模板的非模板成员函数上使用SFINAE是不可能的,因为这将部分地专门化函数,这是不允许的,这要感谢dyp for 然而,正如那篇文章所建议的,您可以非常轻松地使用标记分派 演示代码。注意,我已经将数组更改为使用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"
}
boost有一个enable_if,POD ness maby上的某种SFINAE?你可以在std::Pody上使用SFINAE,这可能是因为你想要的是“可复制的”而不是POD。不管怎样,C++14有一系列有趣的模板,比如is_pod、is_琐碎地_copyable等等。实现只是一个练习,等等……这与user657267的观点一致,即memset可能等于或比std::fill差;另一个选项是通过SFINAE调用reset(如果存在),如果没有,则调用std::fill(如果没有)。boost有一个enable_,如果在POD ness maby上使用某种SFINAE?您可以在std::pody上使用SFINAE。您可能希望“简单可复制”而不是POD。不管怎样,C++14有一系列有趣的模板,比如is_pod、is_琐碎地_copyable等等。实现只是一个练习,等等……这与user657267的观点一致,即memset可能等于或比std::fill差;另一个选项是通过SFINAE调用reset(如果存在),如果不存在则调用std::fill(可能为true);但是他想调用成员函数。为非POD重置,所以他的问题remains@MattMcNabb也许吧,虽然我认为一旦你不再需要区分POD和非POD,假设他想将包含的数据重置为默认状态,那么问题就消失了,也就是说,他应该为更复杂的类提供默认构造函数,而不是重置函数。这是c++11的一个惊人特性。太糟糕了,我受到VC10和CArray dumb T*@liorda当前数据存储方法的限制。你不必使用std::array,你也可以使用任何其他容器,或者标准数组和std::fill,如果你想要堆栈上的内存,甚至只是在数组上循环。关键是memset可能不是答案。@user657267也很正确;从默认构造的xvalue移动应该具有与重置一样好的性能。当然,除非“重置”执行的操作不是将对象恢复到其默认状态,例如,执行对象重置次数计数器!可能是真的;但是他想调用成员函数。为非POD重置,所以他的问题remains@MattMcNabb也许吧,虽然我认为一旦你不再需要区分POD和非POD,假设他想将包含的数据重置为默认状态,那么问题就消失了,也就是说,他应该为更复杂的类提供默认构造函数,而不是重置函数。这是c++11的一个惊人特性。太糟糕了,我受到VC10和CArray dumb T*@liorda当前数据存储方法的限制。你不必使用std::array,你也可以使用任何其他容器,或者标准数组和std::fill,如果你想要堆栈上的内存,甚至只是在数组上循环。关键是memset可能不是答案。@user657267也很正确;从默认构造的xvalue开始移动
具有重置所需的良好性能。当然,除非“重置”执行的操作不是将对象恢复到其默认状态,例如,执行对象重置次数计数器!您的代码中有两个void MemberReset member函数。在上,抱歉。它们看起来太像我了。你的代码中有两个void MemberReset member函数。On,抱歉。他们看起来太像我了。