使用 declype 推断模板元素类型中的数据类型是不是正确?

Posted

技术标签:

【中文标题】使用 declype 推断模板元素类型中的数据类型是不是正确?【英文标题】:Is it correct to use dectlype to deduce the data type in template's element type?使用 declype 推断模板元素类型中的数据类型是否正确? 【发布时间】:2019-09-12 11:27:48 【问题描述】:

您好,我被要求在此示例中模拟算法 std::copy_if,将奇数复制到整数的 list 中,偶数也复制到另一个列表中,所以这是我的示例:

#include <list>
#include <vector>
#include <iostream>

template <typename T>
bool is_odd_pred(T x) 
    return x % 2 != 0;


template <typename T>
bool is_even_pred(T x) 
    return !(x % 2);



template <class T, class U>
void cpy_if(T first, T last, U destIt, bool(*pfnPred)(decltype(*first + 1))) 
    while (first != last) 
        if (pfnPred(*first))
            *destIt++ = *first;
        ++first;
    




int main() 

    std::vector<int> v1 10, 27, 57, 77, 81, 24, 16, 23, 28 ;

    for (auto i : v1)
        std::cout << i << ", ";
    std::cout << std::endl;

    std::list<int> li_odd;
    //cpy_if(v1.cbegin(), v1.cend(), li.begin(),
    //  [](decltype(*v1.cbegin() + 1) a)  return (a % 2 != 0);  );

    cpy_if(v1.cbegin(), v1.cend(), std::back_inserter(li_odd), is_odd_pred);


    for (auto i : li_odd)
        std::cout << i << ", ";
    std::cout << std::endl;

    std::list<int> li_even;

    cpy_if(v1.cbegin(), v1.cend(), std::back_inserter(li_even), is_even_pred);


    for (auto i : li_even)
        std::cout << i << ", ";
    std::cout << std::endl;

该程序看起来运行良好,但是在我的 cpy_if 算法中使用 decltype 的上下文是否正确?因为我只让算法有两种元素类型,它们被认为是迭代器。?感谢您提供任何帮助、提示和建议。

【问题讨论】:

为什么是decltype(*first + 1) 而不是decltype(*first)?不一样吗? @user31264 第二个是参考。 @ItachiUchiwa 收到答案后请不要对问题进行重大更改。 @HolyBlackCat 是的。我还没做。 @ItachiUchiwa 我说的是你编辑了+ 1。 :) 我已将其重新编辑。 【参考方案1】:

明显的问题是*first + 1 不适用于某些类型。

我假设您已添加它以防止 decltype 为您提供参考。如果是这样,更可靠的等价物是:

bool(*pfnPred)(std::remove_cv_t<std::remove_reference_t<decltype(*first)>>)

从C++20开始,可以简写为:

bool(*pfnPred)(std::remove_cvref_t<decltype(*first)>)

接下来,使用函数指针会对谓词施加不必要的限制。

它不允许您使用有状态的 lambda,或参数类型略有不同的函数(例如 bool pred(const int &amp;) ...)。

您可以让您的函数接受任意可调用对象,而不是使用函数指针。 (这就是标准算法的工作原理。)

template <class T, class U, class F>
void cpy_if(T first, T last, U destIt, F pfnPred) 
    while (first != last) 
        if (pfnPred(*first))
            *destIt++ = *first;
        ++first;
    

为了更加安全,您可以使用pfnPred(std::as_const(*first)) 来防止谓词通过非常量引用接受参数并对其进行修改。

【讨论】:

非常感谢!真的很有趣。 但现在我收到此错误:Severity Code Description Project File Line Error C2783 'void cpy_if(T,T,U,F)': could not deduce template argument for 'F' ConsoleApplication1 c:\users\raindrop7\desktop\consoleapplication1\consoleapplication1\consoleapplication1.cpp 110 @ItachiUchiwa 哦,那是因为is_odd_predis_even_pred 是模板。尝试使用 is_odd_pred&lt;int&gt;,或 lambda:[](int x)return x % 2 == 1;【参考方案2】:

decltype(*first)decltype(*first + 1) 相比,它被认为更适合使用

typename iterator_traits<T>::value_type

【讨论】:

这个放在哪里? @ItachiUchiwa 直接代替您的 decltype(...) 构造,不要忘记在 iterator_traits 前面加上 std:: 并包含 &lt;iterator&gt; 标头。【参考方案3】:

您可以将谓词的类型作为模板参数,并使用函数模板参数推导。如需更多灵感(以及可能的实施技巧): https://en.cppreference.com/w/cpp/algorithm/copy

【讨论】:

以上是关于使用 declype 推断模板元素类型中的数据类型是不是正确?的主要内容,如果未能解决你的问题,请参考以下文章

现代C++之理解auto类型推断

C++ Primer 5th笔记(chap 16 模板和泛型编程)模板实参推断

简单数据类型中的模板类型推导[重复]

推断指针非类型模板参数的类型

如何推断 CRTP 中的类型?

调用不带 <> 的模板函数;类型推断