[英]Specializing a function from a variadic template class
考虑这个非法代码:
template <int... Is>
struct Object {
void foo() const;
};
template <int... Js>
void Object<0, Js...>::foo() {/*Do whatever*/}
我们想在第一个模板参数为0时专门化foo(),并且假设我们想要专门化foo(),如果第二个参数是3,第三个int是1.所以我找到的解决方案(不确定是否它是最好的方法)如下:
#include <iostream>
template <int...> struct Foo;
template <int... Is>
struct Object {
int ID; // This member is just to illustrate the case when 'this' is needed in foo().
friend struct Foo<Is...>;
void foo() const {Foo<Is...>::execute(this);} // Pass 'this' in case it is needed.
};
template <int... Is>
struct Foo<0, Is...> {
static void execute (const Object<0, Is...>* object) {std::cout << "First int = 0, ID = " << object->ID << ".\n";}
};
template <int N, int... Is>
struct Foo<N, 3, Is...> {
static void execute (const Object<N, 3, Is...>* object) {std::cout << "Second int = 3, ID = " << object->ID << ".\n";}
};
template <int M, int N, int... Is>
struct Foo<M, N, 1, Is...> {
static void execute (const Object<M, N, 1, Is...>* object) {std::cout << "Third int = 1, ID = " << object->ID << ".\n";}
};
int main() {
Object<0,5,8,2>{4}.foo();
Object<4,3,2,5,3>{2}.foo();
Object<4,2,1>{0}.foo();
}
首先,这个解决方案有什么好处吗? 接下来,如果我们尝试Object<0,3,1,4>{8}.foo();
因为规格不完整。 因此,让我们说最早匹配的专业int将始终优先。 所以在这种情况下Object<0,3,1,4>{8}.foo();
应该运行第一个特化因为0,而Object<9,3,1,4>{8}.foo();
由于3,所以应该进行第二次专业化,依此类推。 如何执行该规则?
评论和提示。
我的方法还可以。 由于我们没有对所有功能进行部分模板专业化。
然后关于Object<0,3,1,4>{8}.foo()
这给出了模糊的部分特化(在Clang 3.6上)。 为了解决这个问题,我最后添加了另一个部分特化
template <int... Is>
struct Foo<0, 3, Is...> {
static void execute (const Object<0, 3, Is...>* object) {std::cout << "First int = 0, second = 3, ID = " << object->ID << ".\n";}
};
另一种可能性是使用std :: integer_sequence。 我现在必须放弃,以下不是解决方案,只是一个开胃菜......
#include <utility>
#include <iostream>
template <class S1, class S2>
struct seq_lt
{
enum {value = 0} ;
} ;
template <int I1, int ...S1, int I2, int ...S2>
struct seq_lt<std::integer_sequence<int, I1, S1...>,
std::integer_sequence<int, I2, S2...>>
{
enum {value = (I1 < I2 ? 1 : 0)} ;
} ;
int main(int argc, char *argv[])
{
std::integer_sequence<int, 1, 2, 3> seq1 ;
std::integer_sequence<int, 2, 3> seq2 ;
std::cout << "seq_lt " << seq_lt<decltype(seq1), decltype(seq2)>::value << std::endl ;
std::cout << "seq_lt " << seq_lt<decltype(seq2), decltype(seq1)>::value << std::endl ;
}
我建议只使用if
语句。 无论如何,编译器可能会优化它们(假设您已启用优化)。
换句话说,只需做这样的事情:
template <int... Js>
void Object::foo() {
std::array<int, sizeof...(Js)> args = {Js...}; // I _think_ this is the correct syntax to dump the parameter pack into an std::array.
if(args.size() > 0 && args[0] == 0) {
// First argument is 0, do whatever.
} else {
// It's not 0, do your other thing.
}
}
你会得到几乎相同的效果,你的代码会更加清晰。
这个解决方案的灵感来自于Marom的第二个建议,也部分受到了celticminstrel解决方案的启发。
#include <iostream>
#include <type_traits>
template <std::size_t, typename T, T...> struct NthValue;
template <typename T, T First, T... Rest>
struct NthValue<0, T, First, Rest...> : std::integral_constant<T, First> {};
template <std::size_t N, typename T, T First, T... Rest>
struct NthValue<N, T, First, Rest...> : NthValue<N - 1, T, Rest...> {};
template <int... Is>
struct Object {
void foo() const {fooHelper (typename Map<Is...>::type{});}
private:
template <int...> struct Map;
template <int, int> struct MappedType {};
struct Default {};
void fooHelper (const MappedType<0,0>&) const {std::cout << "First int = 0.\n";}
void fooHelper (const MappedType<1,3>&) const {std::cout << "Second int = 3.\n";}
void fooHelper (const MappedType<2,1>&) const {std::cout << "Third int = 1.\n";}
void fooHelper (const Default&) const {std::cout << "Default case.\n";}
};
template <int... Ns>
template <int... Is>
struct Object<Ns...>::Map {
using type = typename std::conditional<NthValue<0, int, Is...>::value == 0,
MappedType<0,0>,
typename std::conditional<NthValue<1, int, Is...>::value == 3,
MappedType<1,3>,
typename std::conditional<NthValue<2, int, Is...>::value == 1,
MappedType<2,1>,
Default
>::type
>::type
>::type;
};
int main() {
Object<0,5,8,2>().foo(); // First int = 0.
Object<4,3,2,5,3>().foo(); // Second int = 3.
Object<4,2,1>().foo(); // Third int = 1.
Object<0,3,1,4>().foo(); // First int = 0.
Object<9,3,1,4>().foo(); // Second int = 3.
Object<9,9,9>().foo(); // Default case.
}
也没有运行时开销。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.