C++ 在函数模板参数推导中是否有强制隐式转换的方法?

C++ 在函数模板参数推导中是否有强制隐式转换的方法?,c++,template-argument-deduction,function-templates,C++,Template Argument Deduction,Function Templates,我有一个计数函数模板,它计算该类型向量中某个值的出现次数: 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

我有一个计数函数模板,它计算该类型向量中某个值的出现次数:

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";
}
程序运行得很好,但是我总是需要显式地将第二个参数强制转换或传递给作为第一个参数传递的向量的元素类型的类型

我知道这是由于模板参数推导中的数量有限或允许隐式转换造成的。如果count_u是一个普通的非模板函数,那么它可以正常工作,而无需将文本字符串转换为指向char的常量指针

那么,有没有更好的方法来避免此类强制转换std::stringC++,static_castC++


我想有点像元编程,如果是的话,你能详细说明一个例子吗?

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

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重载,则需要进行一些额外的工作,以便在无法编译时将此模板从重载解析中排除。

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

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重载,则需要进行一些额外的工作,以使此模板在无法编译时从重载解析中排除。

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

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

注意:在本例中,我们很幸运std::vector::value\u type是T的一个方便可用的别名。通常,您可以使用std::type\u identity\u T作为模板推断阻止程序,但这仅在C++20中可用。当然,您可以自己实现它—只有两行

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

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


注意:在本例中,我们很幸运std::vector::value\u type是T的一个方便可用的别名。通常,您可以使用std::type\u identity\u T作为模板推断阻止程序,但这仅在C++20中可用。当然,您可以自己实现它—只有两行

您的专门化似乎已完全中断,将只计算相同的指针值。它看起来有效的唯一原因是编译器的字符串文字优化,但这当然可以随时更改。也许你想先解决这个问题?@SamVarshavchik:怎么做?你认为如果e==value为真,e和value都是常量char*,什么时候会发生?这是一个多项选择突击测验:1当指针值相同时,2当指针指向相同的字符串文字时,即使它们是不同的指针。@SamVarshavchik:Ah!正当我是否使用strcmp?那么,通常如何检查两个常量字符是否指向同一个文本字符串?在这里,您可以用完全相同的方法来修复它。您的专门化似乎已完全中断,只会计算相同的指针值。它看起来有效的唯一原因是编译器的字符串文字优化,但这当然可以随时更改。也许你想先解决这个问题?@SamVarshavchik:怎么做?你认为如果e==value为真,e和value都是常量char*,什么时候会发生?这是一个多项选择突击测验:1当指针值相同时,2当指针指向相同的字符串文字时,即使它们是不同的指针。@SamVarshavchik:Ah!正当我是否使用strcmp?那么,通常如何检查两个常量字符是否指向同一个文本字符串?你用同样的方法修复它,这里。所以第二个版本是一个重载而不是一个专门化,对吗?不,它仍然是一个专门化。它专门化原始模板,第一个参数专门化为const char*,与以前一样。但是专门化不参与函数匹配吗?我是从C++入门学习到的,但是原来的模板仍然是这样。最后一段中的this模板指的是整个模板,而不仅仅是专门化。这个答案中的两个函数是单独的模板,重载相同的函数名。这个答案没有专门性。你不能部分地专门化函数模板。所以第二个版本是重载而不是专门化,对吗?不,它仍然是专门化。它专门化原始模板,第一个参数专门化为const char*,与以前一样。但是专门化不参与函数匹配吗?我是从C++入门学习到的,但是原来的模板仍然是这样。最后一段中的this模板指的是整个模板,而不仅仅是专门化。这个答案中的两个函数是单独的模板,重载相同的函数名。有n个 o这个答案中的专门知识。不能部分地专门化函数模板。