我正在尝试实现一些代码,我想根据类型特征调用某些模板化方法。不幸的是,这些方法是使用与函数不兼容的类型(从不使用所述类型调用)实例化的,这会导致编译错误。理想情况下,我可以使用这些特性来防止所述未调用的方法被实例化,但事实并非如此,所以我正在寻找可以使用的变通方法或设计模式。
我有一些简单的POD结构。每种类型都有不同的成员(它们的存在由相应的特征表示)
struct ThingA
{
int a;
};
struct ThingAB
{
int a;
int b;
};
struct ThingABC
{
int a;
int b;
int c;
};
template <typename Thing>
struct ThingTraits
{
static const bool has_a=false;
static const bool has_b=false;
static const bool has_c=false;
};
这是模板功能; ThingA,ThingAB和ThingABC可以作为Src或Dst类型传递给它
template<typename Src, typename Dst>
void assign(Src const &src, Dst &dst)
{
constexpr bool c_a = ThingTraits<Src>::has_a && ThingTraits<Dst>::has_a;
constexpr bool c_b = ThingTraits<Src>::has_b && ThingTraits<Dst>::has_b;
constexpr bool c_c = ThingTraits<Src>::has_c && ThingTraits<Dst>::has_c;
c_a ? copy_a(src,dst) : do_nothing();
c_b ? copy_b(src,dst) : do_nothing();
c_c ? copy_c(src,dst) : do_nothing();
}
copy_a / b / c方法只复制相应的成员:
template<typename Src, typename Dst>
void copy_a(Src const &src, Dst &dst)
{
dst.a = src.a;
}
template<typename Src, typename Dst>
void copy_b(Src const &src, Dst &dst)
{
dst.b = src.b;
}
template<typename Src, typename Dst>
void copy_c(Src const &src, Dst &dst)
{
dst.c = src.c;
}
当试图编译它时,我得到错误,因为copy_a / b / c被实例化为所需成员不存在的类型(即copy_b)。
我如何实现功能相同的东西,因为这种方式不起作用?我需要ThingA,AB,ABC保持简单的POD,没有其他成员(大小要求严格),我不想确定在运行时调用哪些复制操作。
答案 0 :(得分:4)
你可能有类似的东西:
template<bool hasA>
struct copy_a_caller
{
template <typename Src, typename Dst>
void operator () (const Src& src, Dst& dst) const
{
dst.a = src.a;
}
};
template<>
struct copy_a_caller<false>
{
template <typename Src, typename Dst>
void operator () (const Src&, Dst&) const {}
};
template<typename Src, typename Dst>
void copy_a(Src const &src, Dst &dst)
{
copy_a_caller<ThingTraits<Src>::has_a && ThingTraits<Dst>::has_a>()(src, dst);
}
// similar thing for b and c
然后
template<typename Src, typename Dst>
void assign(Src const &src, Dst &dst)
{
copy_a(src, dst);
copy_b(src, dst);
copy_c(src, dst);
}
答案 1 :(得分:3)
您可以简单地使用委托级别的技巧&amp; (部分)专业化:
template<typename Src, typename Dst>
void assign(Src const &src, Dst &dst)
{
constexpr bool c_a = ThingTraits<Src>::has_a && ThingTraits<Dst>::has_a;
constexpr bool c_b = ThingTraits<Src>::has_b && ThingTraits<Dst>::has_b;
constexpr bool c_c = ThingTraits<Src>::has_c && ThingTraits<Dst>::has_c;
copy_a<c_a>::call(src, dst);
copy_b<c_b>::call(src, dst);
copy_c<c_c>::call(src, dst);
}
template <bool HasA>
struct copy_a;
template <>
struct copy_a<true>
{
template <class Src, class Dst>
static void call(Src const &src, Dst &dst)
{ src.a = dst.a; }
};
template <>
struct copy_a<false>
{
template <class Src, class Dst>
static void call(Src const &, Dst &)
{}
};
copy_b
和copy_c
留给读者作为练习: - )
答案 2 :(得分:2)
虽然比其他答案稍微长一点,但这里有一个使用SFINAE的例子,特别是“有一些成员”的技巧,来完成你想要做的事情:
// Using this as a "type expression container" to force
// the deduction of a type. If said deduction fails,
// the compiler will defer emitting an error message
// until it fails to bind any of the functions below.
//
template<typename nope_t>
struct voidme{using type = void;};
// Using a late return type here allows us to
// reference argument symbols.
//
// Note that the expression within the decltype()
// does not require knowledge of any concrete type.
//
template<typename source_t, typename target_t>
auto copy_a(const source_t &src, target_t &dst)
-> typename voidme<decltype(dst.a = src.a)>::type
{
dst.a = src.a;
}
// A "catch-all" copy_a. If type deduction fails in
// the above version, the compiler will attempt to
// bind this version, which will succeed.
//
// This takes advantage of the "Not An Error" part of SFINAE.
//
void copy_a(...)
{
}
template<typename source_t, typename target_t>
auto copy_b(const source_t &src, target_t &dst)
-> typename voidme<decltype(dst.b = src.b)>::type
{
dst.b = src.b;
}
void copy_b(...)
{
}
template<typename source_t, typename target_t>
auto copy_c(const source_t &src, target_t &dst)
-> typename voidme<decltype(dst.c = src.c)>::type
{
dst.c = src.c;
}
void copy_c(...)
{
}
template<typename source_t, typename target_t>
void assign(const source_t &src, target_t &dst)
{
copy_a(src, dst);
copy_b(src, dst);
copy_c(src, dst);
}
...别处
struct ThingWithA
{
int a;
};
struct ThingWithB
{
int b;
};
struct ThingWithAandB
{
int a;
int b;
};
void DoTheThings()
{
ThingWithA srcA, dstA;
ThingWithB srcB, dstB;
ThingWithAandB srcAB, dstAB;
// assign() wherein "copy_b" and "copy_c" do nothing.
assign(srcA, dstA);
// assign() wherein "copy_a" and "copy_c" do nothing.
assign(srcB, dstB);
// assign() wherein only "copy_c" does nothing.
assign(srcAB, dstAB);
}
请注意,此方法适用于任何类型,并且不需要使用特征。
除非对所涉及的类型有其他限制,否则我的意见您所描述的参数化特征是维护危险(它们需要专门针对需要的每种类型)与assign
一起使用,将其定义暴露给此详细信息。)