C++ std::function 参数 const 引用

问题描述

我想写一个函数,它可以接收一个ostream、一个可迭代对象v和一个函数op。该函数应该对 v 中的每个元素调用 op,然后将结果发送到 ostream。所以我写了下面的代码

template<typename T,typename IN = decltype(*std::declval<T>().begin()),typename FT = function<ostream&(ostream&,const IN&)>
        >
void func(ostream& ss,const T& v,FT op = [](ostream& os,const IN&v)->ostream&{ return os << v; }) {
    for (const auto& i: v) {
        std::invoke(op,ss,i);
    }
}

int main() {
    vector<int> vec = {1,2,3,4};
    func(cout,vec);
    return 0;
}

当我尝试使用 clang++ 编译这些代码时,出现以下错误

 /mnt/d/codes/stlprinter/stack.cpp:42:9: error: no matching function for call to 'invoke'
        std::invoke(op,i);
        ^~~~~~~~~~~
/mnt/d/codes/stlprinter/stack.cpp:48:5: note: in instantiation of function template specialization 'func<std::vector<int,std::allocator<int>>,int &,std::function<std::basic_ostream<char> &(std::basic_ostream<char> &,int &)>>' requested here
    func(cout,vec);
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/functional:85:5: note: candidate template ignored: substitution failure [with _Callable = std::function<std::basic_ostream<char> &(std::basic_ostream<char> &,int &)> &,_Args = <std::basic_ostream<char> &,const int &>]: no type named 'type' in 'std::invoke_result<std::function<std::basic_ostream<char> &(std::basic_ostream<char> &,std::basic_ostream<char> &,const int &>'
    invoke(_Callable&& __fn,_Args&&... __args)
    ^
1 error generated.

错误信息显示调用invoke时,op的类型为_Callable = std::functionstd::basic_ostream &,但op定义为FT = function,为什么第二个参数的'const'说明符丢失了?

解决方法

注意IN,即类型decltype(*std::declval<T>().begin())是一个引用int&;然后对于 const IN&const 在引用上被限定并且被忽略。所以给定的 INint&const IN& -> int& & -> int&

查看 decltype 的行为:

  1. ...
  2. 如果参数是 T 类型的任何其他表达式,并且
    a) ...
    b) 如果表达式的值类别是左值,则 decltype 产生 T&;
    c) ...

您可以通过 std::remove_referencestd::decay 删除引用部分。例如

template<typename T,typename IN = std::remove_reference_t<decltype(*std::declval<T>().begin())>,//                    ^^^^^^^^^^^^^^^^^^^^^^^^                                    ^
        typename FT = function<ostream&(ostream&,const IN&)>
        >
void func(ostream& ss,const T& v,FT op = [](ostream& os,const IN&v)->ostream&{ return os << v; }) {
    for (const auto& i: v) {
        std::invoke(op,ss,i);
    }
}