代码之家  ›  专栏  ›  技术社区  ›  Peter Alexander

自由函数的部分模板专业化-最佳实践

  •  12
  • Peter Alexander  · 技术社区  · 15 年前

    正如大多数C++程序员应该知道的,部分函数的自由化是不允许的。例如,以下是非法的C++:

    template <class T, int N>
    T mul(const T& x) { return x * N; }
    
    template <class T>
    T mul<T, 0>(const T& x) { return T(0); }
    
    // error: function template partial specialization ‘mul<T, 0>’ is not allowed
    

    但是,类/结构的部分模板专用化 允许,并且可以被利用来模仿自由函数的部分模板专门化的功能。例如,上一个示例中的目标可以通过以下方法实现:

    template <class T, int N>
    struct mul_impl
    {
        static T fun(const T& x) { return x * N; }
    };
    
    template <class T>
    struct mul_impl<T, 0>
    {
        static T fun(const T& x) { return T(0); }
    };
    
    template <class T, int N>
    T mul(const T& x)
    {
        return mul_impl<T, N>::fun(x);
    }
    

    它体积更大,更不简洁,但它可以完成任务——对于 mul 考虑到这一点,他们得到了所需的部分专业化。


    我的问题是:在编写模板化的自由函数(供其他人使用)时,您应该自动将实现委托给类的静态方法函数,以便您的库的用户可以随意实现部分专用化,还是只是以正常的方式编写模板化的函数,并接受事实人们不能专门研究他们?

    2 回复  |  直到 15 年前
        1
  •  3
  •   YeahStu    15 年前

    正如Litb所说,ADL在其工作的地方是优越的,这基本上是从调用参数推导模板参数的时候:

    #include <iostream>
    
    namespace arithmetic {
        template <class T, class S>
        T mul(const T& x, const S& y) { return x * y; }
    }
    
    namespace ns {
        class Identity {};
    
        // this is how we write a special mul
        template <class T>
        T mul(const T& x, const Identity&) {
            std::cout << "ADL works!\n";
            return x;
        }
    
        // this is just for illustration, so that the default mul compiles
        int operator*(int x, const Identity&) {
            std::cout << "No ADL!\n";
            return x;
        }
    }
    
    int main() {
        using arithmetic::mul;
        std::cout << mul(3, ns::Identity()) << "\n";
        std::cout << arithmetic::mul(5, ns::Identity());
    }
    

    输出:

    ADL works!
    3
    No ADL!
    5
    

    重载+adl通过部分专门化函数模板来实现您想要的效果 arithmetic::mul 对于 S = ns::Identity . 但它确实依赖于呼叫方以允许ADL的方式呼叫它,这就是为什么你从不打电话的原因。 std::swap 明确地。

    所以问题是,您希望库的用户必须为什么部分地专门化您的函数模板?如果他们要专门化类型(通常是算法模板的情况),请使用ADL。如果他们要专门化整型模板参数,比如在您的示例中,那么我猜您必须委托给一个类。但我通常不希望第三方定义乘3应该做什么-我的库会做。 全部的 整数。我可以合理地期望第三方定义 octonion 会的。

    想想看,求幂可能是一个更好的例子,因为我 算术::mul 令人困惑的类似于 operator* 所以没有必要专门化 mul 在我的例子中。然后我将为第一个参数专门化/adl重载,因为“任何事物的力量的标识都是标识”。不过,希望你能理解。

    我认为ADL有一个缺点——它有效地扁平了名称空间。如果我想使用ADL“实现”两者 arithmetic::sub sandwich::sub 为了我的课,我可能会有麻烦。我不知道专家们对此有什么看法。

    我的意思是:

    namespace arithmetic {
        // subtraction, returns the difference of lhs and rhs
        template<typename T>
        const T sub(const T&lhs, const T&rhs) { return lhs - rhs; }
    }
    
    namespace sandwich {
        // sandwich factory, returns a baguette containing lhs and rhs
        template<typename SandwichFilling>
        const Baguette sub(const SandwichFilling&lhs, const SandwichFilling&rhs) { 
          // does something or other 
        }
    }
    

    现在,我有一种类型 ns::HeapOfHam . 我想利用std::swap-style adl编写我自己的算术::sub实现:

    namespace ns {
        HeapOfHam sub(const HeapOfHam &lhs, const HeapOfHam &rhs) {
            assert(lhs.size >= rhs.size && "No such thing as negative ham!");
            return HeapOfHam(lhs.size - rhs.size);
        }
    }
    

    我还想利用std::swap-style adl编写自己的sandwich::sub实现:

    namespace ns {
        const sandwich::Baguette sub(const HeapOfHam &lhs, const HeapOfHam &rhs) {
            // create a baguette, and put *two* heaps of ham in it, more efficiently
            // than the default implementation could because of some special
            // property of heaps of ham.
        }
    }
    

    等一下。我不能那样做,是吗?具有相同参数和不同返回类型的不同命名空间中的两个不同函数:通常不是问题,这就是命名空间的用途。但我不能告诉他们两个。可能我错过了一些非常明显的东西。

    顺便说一句,在这种情况下,我可以完全专业化 算术::子 三明治::子 .打电话的人会 using 一个或另一个,得到正确的功能。不过,最初的问题是关于部分专门化的,所以如果没有我将heapofham作为类模板,我们可以假装专门化不是一个选项吗?

        2
  •  1
  •   Peter Alexander    15 年前

    如果您正在编写一个库以供其他人或其他人使用,请执行结构/类操作。这是更多的代码,但你的库的用户(可能是未来的你!)会谢谢你的。如果这是一个使用代码,那么失去部分专门化不会对您造成伤害。