【问题标题】:How to call std::unique with custom predicate in C++03?如何在 C++03 中使用自定义谓词调用 std::unique?
【发布时间】:2019-11-05 18:59:24
【问题描述】:

我在 C++11 中看到了这个例子:

std::unique(v.begin(), v.end(), [](float l, float r)
{ 
  return std::abs(l - r) < 0.01; 
});

但是,这在 C++03 中对我来说失败了:

error: template argument for 'template<class _FIter, class _BinaryPredicate> _FIter std::unique(_FIter, _FIter, _BinaryPredicate)' uses local type 'CRayTracer::myFunc()::<lambda(float, float)>'

如何在 C++03 中做到这一点?我认为 Lambda 可能已经存在,并且仿函数/函数对象已经存在,对吧?只是寻找一个简单的解决方案,不需要可扩展 - 它只会在这里使用。

这是一个无法为我编译的代码示例:

#include <iostream>
#include <vector>
#include <algorithm>

int main(){
    std::vector<float> v;
    v.push_back(1.0);
    v.push_back(1.0);
    v.push_back(3.5);
    v.push_back(3.5);

    struct approx_equal
    {
        bool operator()(float l, float r)
        {
            return std::abs(l - r) < 0.01;
        }
    };
    approx_equal f;

    std::unique(v.begin(), v.end(),f);
}

这是它产生的错误:

testUnique.cpp: In function 'int main()':
testUnique.cpp:21:37: error: no matching function for call to 'unique(std::vector<float>::iterator, std::vector<float>::iterator, main()::approx_equal&)'
   21 |     std::unique(v.begin(), v.end(),f);
      |                                     ^
In file included from C:/msys64/mingw64/include/c++/9.2.0/algorithm:62,
                 from testUnique.cpp:3:
C:/msys64/mingw64/include/c++/9.2.0/bits/stl_algo.h:995:5: note: candidate: 'template<class _FIter> _FIter std::unique(_FIter, _FIter)'
  995 |     unique(_ForwardIterator __first, _ForwardIterator __last)
      |     ^~~~~~
C:/msys64/mingw64/include/c++/9.2.0/bits/stl_algo.h:995:5: note:   template argument deduction/substitution failed:
testUnique.cpp:21:37: note:   candidate expects 2 arguments, 3 provided
   21 |     std::unique(v.begin(), v.end(),f);
      |                                     ^
In file included from C:/msys64/mingw64/include/c++/9.2.0/algorithm:62,
                 from testUnique.cpp:3:
C:/msys64/mingw64/include/c++/9.2.0/bits/stl_algo.h:1025:5: note: candidate: 'template<class _FIter, class _BinaryPredicate> _FIter std::unique(_FIter, _FIter, _BinaryPredicate)'
 1025 |     unique(_ForwardIterator __first, _ForwardIterator __last,
      |     ^~~~~~
C:/msys64/mingw64/include/c++/9.2.0/bits/stl_algo.h:1025:5: note:   template argument deduction/substitution failed:
testUnique.cpp: In substitution of 'template<class _FIter, class _BinaryPredicate> _FIter std::unique(_FIter, _FIter, _BinaryPredicate) [with _FIter = __gnu_cxx::__normal_iterator<float*, std::vector<float> >; _BinaryPredicate = main()::approx_equal]':
testUnique.cpp:21:37:   required from here
testUnique.cpp:21:37: error: template argument for 'template<class _FIter, class _BinaryPredicate> _FIter std::unique(_FIter, _FIter, _BinaryPredicate)' uses local type 'main()::approx_equal'
   21 |     std::unique(v.begin(), v.end(),f);
      |                                     ^
testUnique.cpp:21:37: error:   trying to instantiate 'template<class _FIter, class _BinaryPredicate> _FIter std::unique(_FIter, _FIter, _BinaryPredicate)'

这是我的一组标志:

g++ -c -g -O3 -Wp,-D_FORTIFY_SOURCE=2 -m64 -Wshadow -Wall -DMX_COMPAT_32 -fexceptions -fno-omit-frame-pointer -D__WIN32__ -std=c++03 testUnique.cpp -o testUnique.o

【问题讨论】:

  • 非 lambda 函数:在 C++11 中引入。
  • Lambda 是纯粹的语法糖。任何你可以用 lambda 做的事情,你也可以用一个函数对象(可能是模板化的)做(更详细)。
  • 尖刻的评论提醒:“我如何在 C++03 中做到这一点?” - 你为什么不把你的代码移到 C++17 上呢?不要停留在过去。

标签: c++ lambda functor c++03


【解决方案1】:

我认为 Lambda 可能已经存在,

没有。 Lambda 已在 C++11 中引入。

...而且函子/函数对象存在,对吧?

函子只是带有operator() 的对象,因此它们一直存在(尽管不确定何时真正为它们引入了“函子”一词)。

对于正式的正确表达方式,我建议您参考其他参考资料,草率地说这个

auto f = [](float l, float r){ 
  return std::abs(l - r) < 0.01; 
};
f(0.1,0.2);

相当于

struct unnamed {
    bool operator()(float l, float r) {
        return std::abs(l - r) < 0.01; 
    }
};
unnamed f;
f(0.1,0.2);

即您总是可以用手写的仿函数类替换 lambda。创建函子的一个实例并传递它而不是 lambda。

完整示例:

#include <iostream>
#include <vector>
#include <algorithm>

struct approx_equal{
    bool operator()(float l, float r) {
        return std::abs(l - r) < 0.01; 
    }
};

int main(){
    std::vector<float> v{1.0, 1.0, 3.5, 3.5 };

    approx_equal f;

    v.erase(std::unique(v.begin(), v.end(),f),v.end());

    // sorry this is c++11, replace with iterator loop to see output pre-c++11
    for (const auto& x : v) std::cout << x << " ";  
}

PS:在 C++03 中,您不能在本地定义仿函数类,然后将其用作模板参数(注意您没有明确将其作为模板参数传递,但unique 必须从您传递的参数中推断出它的类型中)。

【讨论】:

  • 当我尝试这样做,然后使用std::unique(v.begin(), v.end(), f()),它失败了。 error: no match for call to '(myFunc()::unnamed) ()',还有candidate expects 2 arguments, 0 provided
  • @TylerShellberg 你为什么要通过f()f() 正在尝试调用 operator(),这将返回 bool 但缺少参数。该算法需要f,而不是f()
  • @TylerShellberg btw unnamed 只是为了说明,您通常不知道/关心 lambdas 类型的名称。当你编写仿函数来替换 lambda 时,当然你应该给它一个有意义的名字
  • 我认为提供使用std::unique 和函子(包括正确命名的函子)的工作示例会有助于消除混淆。
  • @foreknownas_463035818 一切都好。是的,看来这只是因为无法在函数范围内定义结构。如果我把它移出来,它工作得很好。谢谢!此外,没有意识到 unique() 实际上并没有删除元素,我阅读的页面中的描述表明并非如此。 Eliminates all but the first element from every consecutive group of equivalent elements from the range [first, last) and returns a past-the-end iterator for the new logical end of the range.
【解决方案2】:

Lambda 在 C++03 中不存在,它们是在 C++11 中引入的。

由于您的 lambda 示例不需要捕获值,您可以将其替换为简单的独立函数,您不需要仿函数,例如:

#include <iostream>
#include <vector>
#include <algorithm>

bool approx_equal(float l, float r) {
    return std::abs(l - r) < 0.01;
}

int main(){
    std::vector<float> v;
    v.push_back(1.0);
    v.push_back(1.0);
    v.push_back(3.5);
    v.push_back(3.5);

    std::unique(v.begin(), v.end(), approx_equal);
}

但是,您也可以使用仿函数。在您尝试使用仿函数时,您只需在 main() 本身内部定义仿函数类型,使其成为编译器抱怨的本地类型。而是在全局范围内定义函子类型(您仍然可以使用局部变量来实例化类型),例如:

#include <iostream>
#include <vector>
#include <algorithm>

struct approx_equal {
    bool operator()(float l, float r) {
        return std::abs(l - r) < 0.01;
    }
};

int main(){
    std::vector<float> v;
    v.push_back(1.0);
    v.push_back(1.0);
    v.push_back(3.5);
    v.push_back(3.5);

    approx_equal f;
    std::unique(v.begin(), v.end(), f);

    // or:
    // std::unique(v.begin(), v.end(), approx_equal{});
}

【讨论】:

  • 不知何故,我认为这样做会更丑陋。它更漂亮,尽管我可能会保留函子,前提是为了确保该函数不会被认为是通用的其他人错误地使用或删除。谢谢!
  • 嗯,我只是很难记住,在 c++03 中,不允许使用本地定义的类作为模板参数。一旦函子没有在本地定义,使用简单的函数当然更好
猜你喜欢
  • 2020-03-15
  • 2016-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
  • 2016-09-22
相关资源
最近更新 更多