"成员是私有的"虽然我在使用尾随返回类型时不从外部访问它

lee*_*mes 8 c++ templates functional-programming metaprogramming c++11

我该如何解决以下问题?

我正在编写一些函数库,它定义了与此问题相关的以下函数:

  • call(f,arg):使用参数调用函数.在某些情况下我只需要一个包装器.
  • comp(f1,f2):返回两个函数的组合.返回一个辅助函子,表示两个函数的组成.

实现如下所示(简化版本仍然证明了问题):

// Call f with one argument
template <class Fn, class Arg>
auto call(const Fn &f, const Arg & arg) -> decltype(f(arg)) {
    return f(arg);
}

// Helper functor for the function below
template<class Fn1, class Fn2>
class CompFn {
    Fn1 a;
    Fn2 b;

public:
    CompFn(const Fn1 &f1, const Fn2 &f2) : a(f1), b(f2) {}

    template<class Arg> inline
    auto operator()(const Arg & arg) const -> decltype(call(b, call(a, arg))) {
        return call(b, call(a, arg));
    }
};

/** Composition of f1 and f2 (f2 after f1). */
template<class Fn1, class Fn2>
CompFn<Fn1,Fn2> comp(const Fn1 &f1, const Fn2 &f2) {
    return CompFn<Fn1,Fn2>(f1, f2);
}
Run Code Online (Sandbox Code Playgroud)

以下代码用作简单测试:

// Example: Take the length of the string and compare it against zero.
std::function<int(std::string)> stringLength = [](std::string s) { return s.size(); };
std::function<bool(int)> greaterZero = [](int x) { return x > 0; };
auto stringNotEmpty = comp(stringLength, greaterZero);

std::string testInput1 = "foo";
std::string testInput2 = "";
Run Code Online (Sandbox Code Playgroud)

直到这里,一切正常.召唤comp自己似乎不是一个问题.直接调用生成的函数也可以.但是通过调用组合call引入了无数的编译错误(yaaay,new record!):

assert(call(stringNotEmpty,testInput1) == true);    // line 44
assert(call(stringNotEmpty,testInput2) == false);
Run Code Online (Sandbox Code Playgroud)

编译输出(gcc 4.7,完整输出见下面的ideone链接):

prog.cpp:16:9: error: ‘std::function<bool(int)> CompFn<std::function<int(std::basic_string<char>)>, std::function<bool(int)> >::b’ is private
prog.cpp:44:5: error: within this context
prog.cpp:15:9: error: ‘std::function<int(std::basic_string<char>)> CompFn<std::function<int(std::basic_string<char>)>, std::function<bool(int)> >::a’ is private
prog.cpp:44:5: error: within this context
prog.cpp:22:10: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) substituting ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’
prog.cpp:22:10:   required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’
prog.cpp:8:6:   required by substitution of ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’
prog.cpp:22:10:   required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’
prog.cpp:8:6:   required by substitution of ‘template<class Fn, class Arg> decltype (f(arg)) call(const Fn&, const Arg&) [with Fn = std::function<int(std::basic_string<char>)>; Arg = std::basic_string<char>]’
prog.cpp:22:10:   required by substitution of ‘template<class Arg> decltype (call(((const CompFn*)this)->CompFn<Fn1, Fn2>::b, call(((const CompFn*)this)->CompFn<Fn1, Fn2>::a, arg))) CompFn::operator()(const Arg&) const [with Arg = Arg; Fn1 = std::function<int(std::basic_string<char>)>; Fn2 = std::function<bool(int)>] [with Arg = std::basic_string<char>]’
prog.cpp:8:6:   [ skipping 890 instantiation contexts ]
[ ...continues endlessly... ]
Run Code Online (Sandbox Code Playgroud)

将组合物转换为a时std::function,它也完全没问题.但这不允许在我的comp函数中使用多态仿函数,至少我看不到一个选项.

一个"修复"是不是decltype使用后返回类型Comp::operator(),但固定收益类型bool(专门用于这种单一测试场景).

所有四个提到的测试用例总结:

  • Test1 - 直接调用合成 - >确定
  • Test2 - 使用- > Error调用合成call
  • Test3 - 将组合强制转换为std :: function,然后使用call- > OK 调用
  • Test4 - 使用调用合成call.固定返回类型Comp::operator()bool- >确定

我的目标是创建call一个"无缝"包装器来调用任何类型的函数:函子,函数指针,成员函数指针,成员变量指针等等,以及使用的组合comp.我有一大堆的重载他们,但我不希望引入的过载Comp<Fn1,Fn2>,因为Fn1还是Fn2可以再次是任何类型的功能,这似乎是一个"递归问题".

Xeo*_*Xeo 6

Clang编译你的失败测试用例就好了,我看不出任何错误,所以我认为这是一个GCC错误.如果可以的话,请用最小的repro(不包括)提交错误报告.

注意:因为call,标准中已经存在类似的东西- INVOKE它不是宏,而是一个概念,可以这么说.它的使用std::bind,std::function和其他的东西,其中之一就是std::reference_wrapper.这意味着你可以做到std::ref(fun)(args...)与之相同call.


Sco*_*nes 2

尝试用 Fn1 的表达式代替 a,用 Fn2 的表达式代替 b 以避免提及私有成员。我在 VC++ 中尝试了这个,但得到了一个不同的错误:

template<class Arg> inline
auto operator()(const Arg & arg) const -> decltype(call(Fn1(), call(Fn2(), arg))) {
    return call(b, call(a, arg));
}
Run Code Online (Sandbox Code Playgroud)