C++ 指定参数类型中是否存在POD结构成员的模板函数
给定POD结构的一般形式C++ 指定参数类型中是否存在POD结构成员的模板函数,c++,templates,c++11,sfinae,C++,Templates,C++11,Sfinae,给定POD结构的一般形式 struct case_0 { const char *foo; }; struct case_1i { const char *foo; int v0; }; struct case_1d { const char *foo; double v0; }; struct case_2ii { const char *foo; int v0; int v1;
struct case_0 { const char *foo; };
struct case_1i { const char *foo; int v0; };
struct case_1d { const char *foo; double v0; };
struct case_2ii { const char *foo; int v0; int v1; };
struct case_2id { const char *foo; int v0; double v1; };
// etc
是否可以根据v0
、v1
等数据成员的存在或不存在,向函数重载集的(模板)成员分派数据(理想情况下,不依赖于这些成员的特定类型),如果是,如何分派?具体地说,给定
void
process(const case_0& c)
{
do_stuff_with(c.foo);
}
template <typename case_1> void
process(const case_1& c)
{
do_stuff_with(c.foo, c.v0);
}
template <typename case_2> void
process(const case_2& c)
{
do_stuff_with(c.foo, c.v0, c.v1);
}
void
过程(const case_0&c)
{
用(c.foo)做东西;
}
模板空隙
过程(施工案例1和c)
{
用(c.foo,c.v0)填充;
}
模板空隙
过程(施工案例2&c)
{
用(c.foo,c.v0,c.v1)填充;
}
我希望为所有case.*
结构选择每个重载,这些结构具有在其主体中使用的所有v-
成员,并且——同样重要的是——没有任何未在其主体中使用的v-
成员
这个程序必须是100%独立的,所以请不要升级。C++11功能还可以。您需要编写一组特性,例如
has_v0
和has_v1
(我确信已经多次演示过这一点),然后使用它们约束重载:
template <typename case_0,
typename = typename std::enable_if<!has_v0<case_0>::value>::type,
typename = typename std::enable_if<!has_v1<case_0>::value>::type
>
void
process(const case_0& c)
{
do_stuff_with(c.foo);
}
template <typename case_1,
typename = typename std::enable_if<has_v0<case_1>::value>::type,
typename = typename std::enable_if<!has_v1<case_1>::value>::type
>
void
process(const case_1& c)
{
do_stuff_with(c.foo, c.v0);
}
template <typename case_2,
typename = typename std::enable_if<has_v0<case_2>::value>::type,
typename = typename std::enable_if<has_v1<case_2>::value>::type
>
void
process(const case_2& c)
{
do_stuff_with(c.foo, c.v0, c.v1);
}
template::type,
typename=typename std::enable_if::value>::type
>
无效的
过程(const case_0&c)
{
用(c.foo)做东西;
}
模板::类型
>
无效的
过程(施工案例1和c)
{
用(c.foo,c.v0)填充;
}
模板
无效的
过程(施工案例2&c)
{
用(c.foo,c.v0,c.v1)填充;
}
您可以使用以下方法简化约束:
template<typename Cond>
using Require = typename std::enable_if<Cond::value>::type;
模板
使用Require=typename std::enable_if::type;
e、 g
模板
无效的
过程(施工案例2&c)
{
用(c.foo,c.v0,c.v1)填充;
}
一个解决方案是@Jonathan Wakely提供的,它使用了has_XXX
元函数
这里是另一个解决方案,但它要求您将完整的结构定义更改为std::tuple
的typedef
完整结构:
替换为typedefs结构,如下所示:
被替换为
一旦接受此设计更改,原始问题的解决方案将变得非常简单,如下所示:
template<size_t...>
struct seq{};
template<size_t M, size_t ...N>
struct genseq : genseq<M-1,M-1, N...> {};
template<size_t ...N>
struct genseq<0,N...>
{
typedef seq<N...> type;
};
template <typename ...Args, size_t ...N>
void call_do_stuff_with(std::tuple<Args...> & tpl, seq<N...>)
{
do_stuff_with(std::get<N>(tpl)...);
}
template <typename ...Args>
void process(std::tuple<Args...> & tpl)
{
const size_t N = sizeof ...(Args);
call_do_stuff_with(tpl, typename genseq<N>::type());
}
模板
结构序列{};
模板
结构genseq:genseq{};
模板
结构genseq
{
typedef-seq-type;
};
模板
void call_do_stuff_with(std::tuple在第一个过程中是否缺少模板
重载?@JonathanWakely只有一个case_0
结构,因此它似乎没有必要,但如果它有助于解决问题,我当然可以添加它(也许是为了利用std::enable_if
)啊,我明白了,我不明白问题的这一部分。当然,我的答案需要一个模板。为什么不为它们定义重载,为每个结构定义一个重载?它们只是少数几个结构!@Nawaz因为通常的原因,人们编写模板函数,而不是写出所有可能的重载:具有v0
但不是v1
是无界的,类似于v0
和v1
但不是v2
,等等。为什么不包括has_v1
的实现呢?对于快速版本:模板结构有_v1:std::false_type{};模板结构有_v1;:type>:std::true{}因为我在火车上,没有时间去寻找或写作one@Zack:是的。如果所有模板参数都是POD,那么实例化的std::tuple
就是POD。看到这个:我不能写std::tuple triples[]={{1,2,3},{4,5,6}我在这台计算机上拥有的任何一个编译器的文件范围内的代码>(我得到编译错误:“使用初始值设定项列表初始化非聚合类型”)。因此,无论std::tuple
是否应该是POD,对于当前编译器实现的这个应用程序来说,它还不够POD。附录:即使这样做有效(没有触发编译错误),我希望当前编译器为它生成静态构造函数,这对于这个应用程序来说也是不可接受的。(上述机器生成的代码包括这些结构数组的初始值设定项列表,其中包含数万个元素。它已经是一个2MB的对象文件。静态构造函数将增加数量级。)@Zack:您可以编写:std::tuple triples[]={std::make_tuple(1,2,3),std::make_tuple(4,5,6)}代码>。我认为这更具可读性,因为它清楚地显示了一个元组对象的构成。这是我所问问题的一个很好的解决方案,将来可能会对其他人有所启发。
struct case_0 { const char *foo; };
struct case_1i { const char *foo; int v0; };
struct case_1d { const char *foo; double v0; };
struct case_2ii { const char *foo; int v0; int v1; };
struct case_2id { const char *foo; int v0; double v1; };
typedef std::tuple<const char*> case_0;
typedef std::tuple<const char*,int> case_1i;
typedef std::tuple<const char*,double> case_1d;
typedef std::tuple<const char*,int,int> case_2ii;
typedef std::tuple<const char*,int,double> case_2id;
template<typename...Args>
auto foo(std::tuple<Args...> & tpl) -> decltype(std::get<0>(tpl))&
{
return std::get<0>(tpl);
}
template<typename...Args>
auto v0(std::tuple<Args...> & tpl) -> decltype(std::get<1>(tpl))&
{
return std::get<1>(tpl);
}
template<typename...Args>
auto v1(std::tuple<Args...> & tpl) -> decltype(std::get<2>(tpl))&
{
return std::get<2>(tpl);
}
case_1i obj; //full-fledge struct
obj.foo = "hello";
obj.v0 = 100;
case_1i obj; //typedef struct
foo(obj) = "hello";
v0(obj) = 100;
template<size_t...>
struct seq{};
template<size_t M, size_t ...N>
struct genseq : genseq<M-1,M-1, N...> {};
template<size_t ...N>
struct genseq<0,N...>
{
typedef seq<N...> type;
};
template <typename ...Args, size_t ...N>
void call_do_stuff_with(std::tuple<Args...> & tpl, seq<N...>)
{
do_stuff_with(std::get<N>(tpl)...);
}
template <typename ...Args>
void process(std::tuple<Args...> & tpl)
{
const size_t N = sizeof ...(Args);
call_do_stuff_with(tpl, typename genseq<N>::type());
}