如何避免反复编写相同的模板?

4
考虑下面这个例子,其中有两个互相依赖的模板类:
template <class T1, class T2, typename = typename std::enable_if<std::is_blah<T1>::value>::type, typename = typename std::enable_if<std::is_blah<T2>::value>::type>
class someClass
{
    // ...

    template <class U1, class U2, typename = typename std::enable_if<is_blah<U1>::value>::type, typename = typename std::enable_if<std::is_blah<U2>::value>::type>
    void fun1(U1 arg1, U2 arg2)
    {
        // ...
    }

    template <class U1, class U2, typename = typename std::enable_if<std::is_blah<U1>::value>::type, typename = typename std::enable_if<std::is_blah<U2>::value>::type>
    void fun2(U1 arg1, U2 arg2)
    {
        // ...
    }

    template <class U1, class U2, typename = typename std::enable_if<std::is_blah<U1>::value>::type, typename = typename std::enable_if<std::is_blah<U2>::value>::type>
    void fun3(U1 arg1, U2 arg2)
    {
        // ...
    }

    // ...
};

没错。在上述情况下,我不得不一遍又一遍地编写相同的模板。不用说这很丑陋,使代码难以阅读,也很繁琐麻烦,需要大量的复制黏贴等等...

显然必须有一个合理的解决方案。

其中之一是使用 #define 定义此模板。我认为这不是什么问题,因为当我完成所有声明和定义时,我可以 #undef 它。但也许这是错误的;我只是一个新手,我看到人们谴责任何 #define。

还有其他解决方案吗?或者这个宏解决方案是最好的选择?


4
我更倾向于使用typedef而不是#define - πάντα ῥεῖ
1
@Ninetainedo 我知道,但据我所知,我不能这样做 typedef template<...> my_template 然后写 my_template class C {} 或者 my_template void f() {} - user4385532
1
typedef 只有在指定类型时才有用,而 (命名不佳的) templateParameters 在代码中被用作占位符。我认为问题是关于如何避免重复模板参数列表的;即 template <typename T1, typename T2, typename T3=blah> 中的部分。这些不是类型,它们是模板参数,因此 typedef 无法帮助。 - Andrew
2
也许我的问题有些令人困惑,对此我感到抱歉。你可以用template <class T1, class T2, typename = typename std::enable_if<is_blah<T1>::value>::type, typename = typename std::enable_if<is_blah<T2>::value>::type>来替换template <someLongAndComplicatedTemplate> - user4385532
2
你的示例代码仍然没有意义。默认模板参数(你用于 SFINAE 的参数)不是签名的一部分,实际上只能被指定一次。 - T.C.
显示剩余13条评论
1个回答

2
我能看到的唯一解决方案是定义一些自定义的“类型函数”和“别名”,以实现两个目标:
  • 使您的代码更简洁,易读(也许);
  • 使维护更容易(即,如您在评论中所指出的,如果您必须进行更改,则只需在一个地方进行即可)
例如,您可以更改原来的代码:
template<typename T1, typename T2, typename = typename std::enable_if<std::is_pod<T1>::value>::type, typename = typename std::enable_if<std::is_pod<T2>::value>::type>
struct C1{};

template<typename T>
using Eif_pod = typename std::enable_if<std::is_pod<T>::value>::type;

template<class T1, class T2, class = Eif_pod<T1>, class = Eif_pod<T2>>
struct C2{};

那很好,但我无法撤销 using,不是吗...我的意思是,这个 Eif_pod 会污染每个包含该文件的文件,这很糟糕,因为 Eif_pod 只是 C1C2 的帮助别名。这就是为什么我在考虑使用宏 - 我可以在完成与 C1C2 相关的任何工作后立即取消定义它们。或者我错了,也许我真的可以撤销 using - user4385532
我认为可以通过使用详细或匿名命名空间来解决这个问题。 - Robert Ramey
@gaazkam C++ 解决作用域污染的方法是命名空间。如果 C1 和 C2 有密切关联,它们应该放在同一个命名空间中,比如说 C。另一方面,像 Eif_pod 这样的东西较为通用,因此可以放在另一个命名空间中,比如说 G。这样,如果您认为有意义,您可以将 G 命名空间的所有内容引入 C 命名空间中,以便更轻松地在 C 中使用 G 的内容。抱歉格式不佳,但我正在使用智能手机。 - Paolo M
好的,谢谢。您能否非常友好地向我解释一下为什么宏解决方案是错误的,因为我认为我没有理解? - user4385532
@gaazkam 宏解决方案并不是错误的。虽然宏不遵守C++的作用域和类型规则,但它们只是在您的代码中将一些字符替换为其他字符。因此,编译器看到的与您所看到的不同。这可能会导致微妙的错误。请参见http://www.stroustrup.com/bs_faq2.html#macro - Paolo M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接