如何处理未调用的函数的不需要的模板实例化?

时间:2014-12-02 07:57:16

标签: c++ templates c++11

我正在尝试实现一些代码,我想根据类型特征调用某些模板化方法。不幸的是,这些方法是使用与函数不兼容的类型(从不使用所述类型调用)实例化的,这会导致编译错误。理想情况下,我可以使用这些特性来防止所述未调用的方法被实例化,但事实并非如此,所以我正在寻找可以使用的变通方法或设计模式。

我有一些简单的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,没有其他成员(大小要求严格),我不想确定在运行时调用哪些复制操作。

3 个答案:

答案 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_bcopy_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一起使用,将其定义暴露给此详细信息。)