我有一个可变模板类SomeClass
,看起来像这样(基本上):
template<std::size_t SIZE_>
class SomeClass {
public:
static constexpr std::size_t SIZE = SIZE_;
};
它有一个constexpr成员,该成员只包含用于实例化它的std::size_t
模板形参。我想要一个constexpr
函数,它可以求和所有SomeClass<SIZE_>
专门化的大小。我的第一个想法是做一个简单的可变模板函数,添加所有的SIZE
,像这样:
template<typename T>
constexpr std::size_t totalSize() {
return T::SIZE;
}
template <typename T, typename... Ts>
constexpr std::size_t totalSize() {
return totalSize<T>() + totalSize<Ts...>();
}
现在我试着叫它:
constexpr std::size_t size = totalSize<SomeClass<1>, SomeClass<2>, SomeClass<3>>() // should return 6
最后一个参数解包使得对totalSize<T>
函数的调用有歧义,因为两个模板totalSize
函数都匹配它。好吧,我修改了我的代码,使其具有两个不同的函数,并提出了这个:
template<typename T>
constexpr std::size_t totalSizeSingle() {
return T::SIZE;
}
template <typename T, typename... Ts>
constexpr std::size_t totalSize() {
std::size_t result = totalSizeSingle<T>();
if (sizeof...(Ts) > 0) result += totalSize<Ts...>();
return result;
}
再一次,最后一次拆包似乎有问题。显然,即使在编译时知道T
,也不能推导出来。我得到以下错误(使用GCC编译):
In instantiation of 'constexpr std::size_t totalSize() [with T = SomeClass<3>; Ts = {}; std::size_t = long long unsigned int]':
main.cpp:129:51: required from here
main.cpp:134:82: in 'constexpr' expansion of 'totalSize<SomeClass<1>, SomeClass<2>, SomeClass<3> >()'
main.cpp:129:51: in 'constexpr' expansion of 'totalSize<SomeClass<2>, SomeClass<3> >()'
main.cpp:129:58: error: no matching function for call to 'totalSize<>()'
129 | if (sizeof...(Ts) > 0) result += totalSize<Ts...>();
| ~~~~~~~~~~~~~~~~^~
main.cpp:127:23: note: candidate: 'template<class T, class ... Ts> constexpr std::size_t totalSize()'
127 | constexpr std::size_t totalSize() {
| ^~~~~~~~~
main.cpp:127:23: note: template argument deduction/substitution failed:
main.cpp:129:58: note: couldn't deduce template parameter 'T'
129 | if (sizeof...(Ts) > 0) result += totalSize<Ts...>();
| ~~~~~~~~~~~~~~~~^~
我真的不明白为什么它不工作。每种类型在编译时都是已知的。为什么会发生这种情况,我怎样才能让它工作?我正在使用c++ 11.
在c++模板元编程中,人们称struct
为元函数是有原因的。
在我们的例子中,在结构体中进行元编程的主要优点是,通过模板参数解决函数重载与类专门化解决函数重载不同,而且更受限制。
所以我建议:
template <class ...> // only called if there's no arguments
struct add_sizes {
static constexpr auto value = 0;
};
template <class T, class ... Ts>
struct add_sizes <T, Ts...> {
static constexpr auto value = T::SIZE + add_sizes<Ts...>::value;
};
// then wrap it into your function:
template <class ... Ts>
constexpr std::size_t totalSize () {
return add_sizes<Ts...>::value;
}
演示使用结构体的另一个优点是便于"返回";多个值或类型,这对于函数来说很复杂。有时通过计算A你已经计算过B
总的来说,如果你在用函数解决元编程问题时遇到困难,那就改用结构体吧。
问题是当模板参数列表的大小为1时,totalSize<Ts...>();
将调用totalSize<>()
,但totalSize<>();
没有定义。
你可以让你的第一个版本使用2个模板参数。
template<typename T>
constexpr std::size_t totalSize() {
return T::SIZE;
}
template <typename T, typename U, typename... Ts>
constexpr std::size_t totalSize() {
return totalSize<T>() + totalSize<U, Ts...>();
}
演示