有没有办法在函数模板参数推导中强制隐式转换?

Is there a way to force implicit conversion in function template argument deduction?

我有这个 count_ 函数模板,它计算一个值在其类型的向量中出现的次数:

template <typename T>
std::size_t count_(std::vector<T> const& vt, T const& value)
{
    std::cout << "count_(vector<T>, T const&)\n";
    std::size_t n{};
    for(auto const& e : vt)
        if(e == value)
            ++n;
    return n;
}

template <>
std::size_t count_(std::vector<char const*> const& vcp, char const * const& value)
{
    std::cout << "count_(vector<char const*>, char const*const&)\n";

    std::size_t n{};
    for(auto const& e : vcp)
        if( !strcmp(e, value))
            ++n;
    return n;
}



int main()
{

    std::vector<std::string> vs{"function", "C++", "template", "C++", "specialization", "partial", "C++", "full"};
    std::cout << count_(vs, std::string("C++")) << '\n';

    std::vector<double> vd{3.14, 5.2, 7.7, 3.14, 56.87, 3.14, 6.8798, 12.545};
    std::cout << count_(vd, 3.14) << '\n';

    std::cout << count_(std::vector{7, 24, 16, 7, 81, 7, 5, 7, 23, 10, 7, 15, 8}, 7) << '\n';

    std::vector<char const*> vcp{"function", "C++", "template", "C++", "specialization", "partial", "C++", "full"};
    std::cout << count_(vcp, static_cast<char const*>("C++")) << '\n';


    std::cout << "\ndone!\n";
}

那么有没有更好的方法来避免这种转换std::string("C++")static_cast<char const*>("C++")...?

如果可以的话,我想应该有类似“元编程”的东西,请您详细说明一个例子好吗?

最简单的解决方案是使用转发引用:

template <typename T, typename Value>
std::size_t count_(std::vector<T> const& vt, Value &&value)
{
    std::cout << "count_(vector<T>, T const&)\n";
    std::size_t n{};
    for(auto const& e : vt)
        if(e == value)
            ++n;
    return n;
}

有了这个,count_(vs, "C++") 就可以编译了。

请注意,专业化需要相应调整:

template <typename Value>
std::size_t count_(std::vector<char const*> const& vcp, Value && value)

// ...

您需要记住,此模板现在将在比以前更多的情况下参与重载决议(由于转发引用)。如果这是不可取的(如果有 count_ 的其他重载),则需要一些额外的工作来使此模板在无法编译时从重载解析中排除。

命令从向量中推导出模板参数,而不是从值中推导出

template<typename T> //                                             v      only required change       v
typename std::vector<T>::size_type count_(std::vector<T> const &vt, typename std::vector<T>::value_type const &value) {
    std::cout << "count_(vector<T> const&, T const&)\n";
    typename std::vector<T>::size_type n = 0;
    for(auto const &e : vt) if(e == value) n++;
    return n;
}

模板参数推导基本上无法通过类型别名“看到”,尤其是那些属于未知专业化成员的别名。 count_ 的第二个参数不再参与推导 T。相反,vector 总是决定 T,然后第二个参数被隐式转换为其类型。您的专业仍然是专业。实际上,它并没有改变。

// just cosmetic changes
template<>
std::vector<char const*>::size_type count_(std::vector<char const*> const &vcp, char const *const &value) {
    std::cout << "count_(vector<char const*> const&, char const *const&)\n";
    std::vector<char const*>::size_type n = 0;
    for(auto const &e : vcp) if(!std::strcmp(e, value)) n++;
    return n;
}

Godbolt

注意:在这种情况下,我们很“幸运”std::vector<T>::value_typeT 的方便可用的别名。通常,您可以使用 std::type_identity_t<T> 作为模板推导阻止程序,但这仅在 C++20 中可用。 (当然你也可以自己实现,两行就行!)