【问题标题】:Ambiguous overload with functor base classes带有仿函数基类的模棱两可的重载
【发布时间】:2017-08-30 03:44:49
【问题描述】:

我试图在此处编写与 overload 结构相同的代码 http://en.cppreference.com/w/cpp/utility/variant/visit 并将其扩展为也可以使用函数。

这里是下面转载的代码https://wandbox.org/permlink/5Z2jsEjOewkGoPeX

#include <utility>
#include <type_traits>
#include <cassert>
#include <string>

namespace {
    template <typename Func>
    class OverloadFuncImpl : public Func {
    public:
        template <typename F>
        explicit OverloadFuncImpl(F&& f) : Func{std::forward<F>(f)} {}
        using Func::operator();
    };
    template <typename ReturnType, typename... Args>
    class OverloadFuncImpl<ReturnType (*) (Args...)> {
    public:
        template <typename F>
        explicit OverloadFuncImpl(F&& f) : func{std::forward<F>(f)} {}
        ReturnType operator()(Args... args) {
            return this->func(args...);
        }
    private:
        ReturnType (*func) (Args...);
    };

    template <typename... Funcs>
    class Overload;
    template <typename Func, typename... Funcs>
    class Overload<Func, Funcs...>
            : public OverloadFuncImpl<Func>,
              public Overload<Funcs...> {
    public:
        template <typename F, typename... Fs>
        explicit Overload(F&& f, Fs&&... fs)
            : OverloadFuncImpl<Func>{std::forward<F>(f)},
            Overload<Funcs...>{std::forward<Fs>(fs)...} {}
        using OverloadFuncImpl<Func>::operator();
        using Overload<Funcs...>::operator();
    };
    template <typename Func>
    class Overload<Func> : public OverloadFuncImpl<Func> {
    public:
        template <typename F>
        explicit Overload(F&& f) : OverloadFuncImpl<Func>{std::forward<F>(f)} {}
        using OverloadFuncImpl<Func>::operator();
    };
}

template <typename... Funcs>
auto make_overload(Funcs&&... funcs) {
    return Overload<std::decay_t<Funcs>...>{std::forward<Funcs>(funcs)...};
}

char foo(char ch) {
    return ch;
}

int main() {
    auto overloaded = make_overload(
        [&](int integer) { return integer; },
        [&](std::string str) { return str; },
        [&](double d) { return d; },
        foo);

    assert(overloaded("something") == "something");
    assert(overloaded(1.1) == 1.1);

    return 0;
}

这是我得到的错误

In file included from /opt/wandbox/gcc-7.2.0/include/c++/7.2.0/cassert:44:0,
                 from prog.cc:3:
prog.cc: In function 'int main()':
prog.cc:66:26: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
     assert(overloaded(1.1) == 1.1);
                          ^
prog.cc:62:21: note: candidate 1: main()::<lambda(double)>
         [&](double d) { return d; },
                     ^
prog.cc:19:20: note: candidate 2: ReturnType {anonymous}::OverloadFuncImpl<ReturnType (*)(Args ...)>::operator()(Args ...) [with ReturnType = char; Args = {char}]
         ReturnType operator()(Args... args) {
                    ^~~~~~~~

编译器和标准解释存在几个问题,需要一一导入operator()。但不知何故,OverloadFuncImpl 的函数特化的operator() 似乎没有通过using 正确导入。

请注意,当我不使用 OverloadFuncImpl 或排除 OverloadFuncImpl 的函数部分特化时,上面的代码可以正常工作。

我已经让这段代码使用解决方法,但我只是想知道为什么上面的代码不起作用。我似乎无法弄清楚......为什么当我导入所有基类的所有operator() 时。还是有模棱两可的重载问题?

我试图在较小的上下文中重现该错误,但无法...

【问题讨论】:

  • 顺便说一句,这与您的问题没有直接关系,但是:您的函数指针实现还有其他问题。只需尝试从采用右值引用的函数指针创建重载。
  • @NirFriedman 你在说哪一部分?构造函数还是调用运算符?
  • 是的,这就是我要建议的。使用 enable_if 进行模板化,您可以在 body 中使用的同一个调用上执行 decltype,这相对简单,我认为可以很好地解决所有非疯狂情况。
  • 因为这个问题可能对未来的用户有帮助,而且问题很有趣,IMO,你可以。

标签: c++ templates c++14 overloading template-specialization


【解决方案1】:
    ReturnType operator()(Args... args) const {
//                                      ^^^^^
        return this->func(args...);
    }

实际上,所讨论的重载集中的相关候选者是

 char operator()(char);
 double operator()(double) const;

使用double 类型的参数调用非const 对象。

在隐式对象参数上先胜;第二个在实际函数参数上获胜。模棱两可随之而来。

【讨论】:

  • 知道为什么错误消息没有提到这一点吗?
  • @Curious 我想很容易忽略的一点是,lambda 的 operator() 是隐式的 const。这就是为什么 lambda 在必要时必须声明为可变的原因。这使得 TCs 的解决方案不会立即跳出(至少,当我阅读错误消息时,不会)。
  • @NirFriedman 是的,我明白了,我自己从来没有想过这个:(
  • @T.C.这是一个很好的收获,我将在 2 天后奖励 100 或 50 点赏金,当它有资格获得赏金时:)
猜你喜欢
  • 1970-01-01
  • 2019-04-27
  • 1970-01-01
  • 1970-01-01
  • 2017-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
相关资源
最近更新 更多