简体   繁体   English

自由功能的部分模板专业化 - 最佳实践

[英]Partial template specialization of free functions - best practices

As most C++ programmers should know, partial template specialization of free functions is disallowed. 正如大多数C ++程序员应该知道的那样,不允许对自由函数进行部分模板特化。 For example, the following is illegal 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

However, partial template specialization of classes/structs is allowed, and can be exploited to mimic the functionality of partial template specialization of free functions. 然而,类/结构的模板偏特允许的,并且可以被利用来模仿的自由功能模板偏特的功能。 For example, the target objective in the last example can be achieved by using: 例如,最后一个示例中的目标目标可以通过以下方式实现:

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);
}

It's more bulky and less concise, but it gets the job done -- and as far as users of mul are concerned, they get the desired partial specialization. 它更笨重,更简洁,但它完成了工作 - 就mul用户而言,他们获得了所需的部分专业化。


My questions is: when writing templated free functions (that are intended to be used by others), should you automatically delegate the implementation to a static method function of a class, so that users of your library may implement partial specializations at will, or do you just write the templated function the normal way, and live with the fact that people won't be able to specialize them? 我的问题是:在编写模板化的自由函数(打算由其他人使用)时,你应该自动将实现委托给类的静态方法函数,这样你的库的用户可以随意实现部分特化,或者做你只是以正常的方式写出模板化的函数,并且生活在人们将无法专门化它们的事实中?

As litb says, ADL is superior where it can work, which is basically whenever the template parameters can be deduced from the call parameters: 正如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());
}

Output: 输出:

ADL works!
3
No ADL!
5

Overloading+ADL achieves what you would have achieved by partially specializing the function template arithmetic::mul for S = ns::Identity . 重载+ ADL通过部分专门化S = ns::Identity的函数模板arithmetic::mul来实现您的目标。 But it does rely on the caller to call it in a way which allows ADL, which is why you never call std::swap explicitly. 但它确实依赖于调用者以允许ADL的方式调用它,这就是为什么你从不显式调用std::swap

So the question is, what do you expect users of your library to have to partially specialize your function templates for? 所以问题是,您希望您的库用户必须部分专门化您的功能模板? If they're going to specialize them for types (as is normally the case with algorithm templates), use ADL. 如果他们要将它们专门用于类型(通常是算法模板的情况),请使用ADL。 If they're going to specialize them for integer template parameters, as in your example, then I guess you have to delegate to a class. 如果他们要将它们专门用于整数模板参数,就像在你的例子中一样,那么我猜你必须委托给一个类。 But I don't normally expect a third party to define what multiplication by 3 should do - my library will do all the integers. 但我通常不希望第三方定义乘以3的乘法 - 我的库将执行所有整数。 I could reasonably expect a third party to define what multiplication by an octonion will do. 我可以合理地期望第三方定义一个八进制乘法会做什么。

Come to think of it, exponentiation might have been a better example for me to use, since my arithmetic::mul is confusingly similar to operator* , so there's no actual need to specialize mul in my example. 考虑一下,取幂可能是我使用的一个更好的例子,因为我的arithmetic::muloperator*混淆相似,所以在我的例子中没有实际需要专门化mul Then I'd specialize/ADL-overload for the first parameter, since "Identity to the power of anything is Identity". 然后我会为第一个参数专门化/ ADL重载,因为“身份对任何东西的认同都是身份”。 Hopefully you get the idea, though. 但是,希望你能得到这个想法。

I think there is a downside to ADL - it effectively flattens namespaces. 我认为ADL有一个缺点 - 它有效地平滑了命名空间。 If I want to use ADL to "implement" both arithmetic::sub and sandwich::sub for my class, then I could be in trouble. 如果我想使用ADL为我的类“实现” arithmetic::subsandwich::sub ,那么我可能会遇到麻烦。 I don't know what the experts have to say about that. 我不知道专家们对此有何看法。

By which I mean: 我的意思是:

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 
    }
}

Now, I have a type ns::HeapOfHam . 现在,我有一个类型ns::HeapOfHam I want to take advantage of std::swap-style ADL to write my own implementation of arithmetic::sub: 我想利用std :: swap-style ADL编写我自己的arithmetic :: 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);
    }
}

I also want to take advantage of std::swap-style ADL to write my own implementation of sandwich::sub: 我也想利用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.
    }
}

Hang on a minute. 等一下。 I can't do that, can I? 我不能那样做,可以吗? Two different functions in different namespaces with the same parameters and different return types: not usually a problem, that's what namespaces are for. 具有相同参数和不同返回类型的不同命名空间中的两个不同函数:通常不是问题,这就是命名空间的用途。 But I can't ADL-ify them both. 但我不能ADL-ify他们俩。 Possibly I'm missing something really obvious. 可能我错过了一些非常明显的东西。

Btw, in this case I could just fully specialize each of arithmetic::sub and sandwich::sub . 顺便说一句,在这种情况下,我可以完全专门化每个arithmetic::subsandwich::sub Callers would using one or the other, and get the right function. 呼叫者将using其中一个,并获得正确的功能。 The original question talks about partial specialization, though, so can we pretend that specialization is not an option, without me actually making HeapOfHam a class template? 最初的问题谈到部分专业化,所以我们可以假装专业化不是一种选择,没有我实际上让HeapOfHam成为一个类模板吗?

If you are writing a library to be use elsewhere or by other people do the struct/class thing. 如果您正在编写一个库以供其他人使用或由其他人使用,请执行struct / class。 It is more code but the users of your library (possibly a future you!) will thank you. 这是更多的代码,但你的图书馆的用户(可能是你的未来!)会感谢你。 IF this is one use code, the loss of partial specialization will not hurt you. 如果这是一个使用代码,部分专业化的损失不会伤害你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM