与函子基类的模棱两可的超负荷

Ambiguous overload with functor base classes

本文关键字:模棱两可 超负荷 基类      更新时间:2023-10-16

我试图编写与 overload struct相同的代码。http://en.cppreference.com/w/cpp/utility/variant/variant/visit并将其扩展到功能也是如此。

这是代码https://wandbox.org/permlink/5z2jsejoewkgopex reproded

#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()时。仍然有模棱两可的超负荷问题?

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

    ReturnType operator()(Args... args) const {
//                                      ^^^^^
        return this->func(args...);
    }

有效地,有效集合中的相关候选人是

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

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

隐式对象参数上的第一个胜利;第二个胜利是实际函数参数。随之而来的歧义。