如何处理空参数包的情况
How to handle a case of empty parameter pack
我有以下代码用于获取包中所有基本类型的大小(我以特殊方式处理浮点数和双精度数),但是当包为空时我的程序无法编译。
// terminating general case for T being anything else
template <class T>
size_t getSize()
{
return sizeof(T);
}
// terminating special case for T=double
template<>
size_t getSize<double>()
{
return SIZEOF_DOUBLE;
}
// terminating special case for T=float
template<>
size_t getSize<float>()
{
return SIZEOF_FLOAT;
}
// recursive case
template <class T, class U, class ...S>
size_t getSize()
{
return getSize<T>() + getSize<U, S...>();
}
我希望 getSize
到 return 0
被称为
template <class ...T>
void foo(T... arg)
{
size_t sizeOfTypes = getSize<T...>();
}
与T={}
,即foo
被称为foo<>();
。
请注意,我不想修改 foo
的调用方式,尖括号必须保留在那里。最好我想修改 getSize
,因为我在 foo
以外的几个函数中使用它。不得已修改foo
。
首先是一个将单一类型转换为其大小的函数模板,可能具有特殊化:
template <class T>
constexpr size_t getSize_single()
{
return sizeof(T);
}
template<>
constexpr size_t getSize_single<double>()
{
return SIZEOF_DOUBLE;
}
template<>
constexpr size_t getSize_single<float>()
{
return SIZEOF_FLOAT;
}
接下来, pack 将类型包扩展为 std::initializer_list<size_t>
大小,然后对其求和:
template <class... Ts>
constexpr size_t getSize() // constexpr for C++14 only - remove for C++11
{
std::initializer_list<size_t> l{getSize_single<Ts>()...};
size_t sum = 0;
for(auto s : l) sum += s;
return sum;
}
需要 initializer_list
的显式类型来处理空包情况。
解决这个问题的另一种方法是使用一些小结构,例如
template<typename T>
struct GetTypeSize
{
enum { value = sizeof(T) };
};
template<>
struct GetTypeSize<float>
{
enum { value = SIZEOF_FLOAT };
};
template<>
struct GetTypeSize<double>
{
enum { value = SIZEOF_DOUBLE };
};
template<typename...>
struct GetSize
{
enum { value = 0 };
};
template<typename Head, typename... Tail>
struct GetSize<Head, Tail...>
{
enum { value = GetTypeSize<Head>::value + GetSize<Tail...>::value };
};
template<typename... T>
void foo(T... arg)
{
size_t sizeOfTypes = GetSize<T...>::value;
}
这具有在编译期间进行评估(总结)的优点。
我使用了两种结构。一个用于执行递归 (GetSize),另一个用于获取类型的实际大小 (GetTypeSize)。 GetSize<Head, Tail...>
的特化只要有head(pack不为空)就实例化,将Head中类型的大小加到GetSize<Tail...>
的递归调用中。一旦没有 Head,就会使用后备 GetSize 模板。
对于实例化
GetSize<int, double, char>
结果是
GetTypeSize<int>::value + GetTypeSize<double>::value + GetTypeSize<char>::value + GetSize<>::value
然后是
sizeof(int) + SIZEOF_DOUBLE + sizeof(char) + 0
如果您只想编辑 getSize 函数,则将第一个模板 T 初始化为 void,并为 void 类型添加一个额外的模板函数到 return 0。它看起来像这样:
template <class T=void> ////A little edit here
size_t getSize()
{
return sizeof(T);
}
// terminating special case for T=double
template<>
size_t getSize<double>()
{
return SIZEOF_DOUBLE;
}
// terminating special case for T=float
template<>
size_t getSize<float>()
{
return SIZEOF_FLOAT;
}
/////Extra entry for void type
template<>
size_t getSize<void>()
{
return 0;
}
当调用 foo<>() 时,T 将默认设置为 void 并且将调用最后一个函数而不是其他函数,并且它将 return 0 作为大小。
我有以下代码用于获取包中所有基本类型的大小(我以特殊方式处理浮点数和双精度数),但是当包为空时我的程序无法编译。
// terminating general case for T being anything else
template <class T>
size_t getSize()
{
return sizeof(T);
}
// terminating special case for T=double
template<>
size_t getSize<double>()
{
return SIZEOF_DOUBLE;
}
// terminating special case for T=float
template<>
size_t getSize<float>()
{
return SIZEOF_FLOAT;
}
// recursive case
template <class T, class U, class ...S>
size_t getSize()
{
return getSize<T>() + getSize<U, S...>();
}
我希望 getSize
到 return 0
被称为
template <class ...T>
void foo(T... arg)
{
size_t sizeOfTypes = getSize<T...>();
}
与T={}
,即foo
被称为foo<>();
。
请注意,我不想修改 foo
的调用方式,尖括号必须保留在那里。最好我想修改 getSize
,因为我在 foo
以外的几个函数中使用它。不得已修改foo
。
首先是一个将单一类型转换为其大小的函数模板,可能具有特殊化:
template <class T>
constexpr size_t getSize_single()
{
return sizeof(T);
}
template<>
constexpr size_t getSize_single<double>()
{
return SIZEOF_DOUBLE;
}
template<>
constexpr size_t getSize_single<float>()
{
return SIZEOF_FLOAT;
}
接下来, pack 将类型包扩展为 std::initializer_list<size_t>
大小,然后对其求和:
template <class... Ts>
constexpr size_t getSize() // constexpr for C++14 only - remove for C++11
{
std::initializer_list<size_t> l{getSize_single<Ts>()...};
size_t sum = 0;
for(auto s : l) sum += s;
return sum;
}
需要 initializer_list
的显式类型来处理空包情况。
解决这个问题的另一种方法是使用一些小结构,例如
template<typename T>
struct GetTypeSize
{
enum { value = sizeof(T) };
};
template<>
struct GetTypeSize<float>
{
enum { value = SIZEOF_FLOAT };
};
template<>
struct GetTypeSize<double>
{
enum { value = SIZEOF_DOUBLE };
};
template<typename...>
struct GetSize
{
enum { value = 0 };
};
template<typename Head, typename... Tail>
struct GetSize<Head, Tail...>
{
enum { value = GetTypeSize<Head>::value + GetSize<Tail...>::value };
};
template<typename... T>
void foo(T... arg)
{
size_t sizeOfTypes = GetSize<T...>::value;
}
这具有在编译期间进行评估(总结)的优点。
我使用了两种结构。一个用于执行递归 (GetSize),另一个用于获取类型的实际大小 (GetTypeSize)。 GetSize<Head, Tail...>
的特化只要有head(pack不为空)就实例化,将Head中类型的大小加到GetSize<Tail...>
的递归调用中。一旦没有 Head,就会使用后备 GetSize 模板。
对于实例化
GetSize<int, double, char>
结果是
GetTypeSize<int>::value + GetTypeSize<double>::value + GetTypeSize<char>::value + GetSize<>::value
然后是
sizeof(int) + SIZEOF_DOUBLE + sizeof(char) + 0
如果您只想编辑 getSize 函数,则将第一个模板 T 初始化为 void,并为 void 类型添加一个额外的模板函数到 return 0。它看起来像这样:
template <class T=void> ////A little edit here
size_t getSize()
{
return sizeof(T);
}
// terminating special case for T=double
template<>
size_t getSize<double>()
{
return SIZEOF_DOUBLE;
}
// terminating special case for T=float
template<>
size_t getSize<float>()
{
return SIZEOF_FLOAT;
}
/////Extra entry for void type
template<>
size_t getSize<void>()
{
return 0;
}
当调用 foo<>() 时,T 将默认设置为 void 并且将调用最后一个函数而不是其他函数,并且它将 return 0 作为大小。