c++ - "Member is private"虽然我不从外部访问它,但在使用尾随返回类型时

标签 c++ templates c++11 functional-programming metaprogramming

如何解决以下问题?

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

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

以下代码用作简单测试:

// 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 = "";

到这里为止,一切正常。打电话comp本身似乎没有问题。直接调用结果函数也可以。但是通过 call 调用组合引入无限数量的编译错误(yaaay,新记录!):

assert(call(stringNotEmpty,testInput1) == true);    // line 44
assert(call(stringNotEmpty,testInput2) == false);

编译输出(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... ]

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

一个“修复”是不使用带有 decltype 的尾随返回类型 Comp::operator() , 但将返回类型固定为 bool (专用于此单一测试场景)。

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

  • Test1 -- 直接调用组合 --> OK
  • Test2 -- 使用 call 调用组合-->错误
  • Test3 -- 将组合转换为 std::function,然后使用 call 调用--> 好的
  • Test4 -- 使用 call 调用组合. Comp::operator() 的固定返回类型至 bool --> 好的

我的目标是制作call调用任何类型函数的“无缝”包装器:仿函数、函数指针、成员函数指针、成员变量指针等...,以及使用 comp 的组合.我有一堆重载,但我不想为 Comp<Fn1,Fn2> 引入重载自 Fn1Fn2又可以是任何类型的函数,这似乎是一个“递归问题”。

最佳答案

Clang 可以很好地编译您失败的测试用例,我看不到任何错误,所以我认为这是一个 GCC 错误。如果可以,请提交一份带有最少重现(不包含)的错误报告。

注意:对于call,标准中已经有likeINVOKE,不是宏,而是概念,可以这么说。它被 std::bindstd::function 和其他东西使用,其中之一是 std::reference_wrapper。这意味着您可以执行 std::ref(fun)(args...) 来实现与 call 相同的效果。

关于c++ - "Member is private"虽然我不从外部访问它,但在使用尾随返回类型时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16327796/

相关文章:

c++ - 需要更有效的解决方案

c++ - 在通用对象更新循环中,按 Controller 更新还是按对象更新更好?

c++ - 模板难题

c++ - 如何检查程序正在写入终端

c++ - 混合 C++11 原子和 OpenMP

c++ - std::string::reserve() 和 std::string::clear() 难题

c++ - 如何在高于 2D 的维度上旋转、旋转、螺旋

c++ - 我应该如何将一个值插入另一个的 "middle"?

c++ - 如何转义C++字符串中的分号

C++ 模板 - 成员函数的部分特化