标识哪个类提供由重载决策选择的函数的类型特征

Vin*_*ent 6 c++ overloading functor template-meta-programming c++17

考虑以下代码,其中一个仿函数derived继承自两个基类base1,base2每个基类提供不同的重载:

// Preamble
#include <iostream>
#include <functional>
#include <type_traits>

// Base 1
struct base1 {
    void operator()(int) const {
        std::cout << "void base1::operator()(int) const\n";
    }
    void operator()(double) const {
        std::cout << "void base1::operator()(double) const\n";
    }
    template <class Arg, class... Args>
    void operator()(const Arg&, Args&&...) const {
        std::cout << "void base1::operator()(const Arg&, Args&&...) const\n";
    }
};

// Base 2
struct base2 {
    void operator()(int) {
        std::cout << "void base2::operator()(int)\n";
    }
    void operator()(double) {
        std::cout << "void base2::operator()(double)\n";
    }
};

// Derived
struct derived: base1, base2 {
    using base1::operator();
    using base2::operator();
    void operator()(char) {
        std::cout << "void derived::operator()(char)\n";
    }
};

// Call
template <class F, class... Args>
void call(F&& f, Args&&... args) {
    std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}

// Main
int main(int argc, char* argv[]) {
    const derived d1;
    derived d2;
    derived d3;
    call(d1, 1);    // void base1::operator()(int) const
    call(d2, 1);    // void base2::operator()(int)
    call(d1, 1, 2); // void base1::operator()(const Arg&, Args&&...) const
    call(d2, 1, 2); // void base1::operator()(const Arg&, Args&&...) const
    call(d3, 'a');  // void derived::operator()(char)
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果输出是:

void base1::operator()(int) const
void base2::operator()(int)
void base1::operator()(const Arg&, Args&&...) const
void base1::operator()(const Arg&, Args&&...) const
void derived::operator()(char)
Run Code Online (Sandbox Code Playgroud)

这说明根据参数,选定的重载可以来自base1,base2derived.

我的问题是:是否有可能在编译时通过创建一个类型特征来检测哪个类提供了所选的重载?

这将具有以下形式:

template <
    class Base1,   // Possibly cv-ref qualified base1
    class Base2,   // Possibly cv-ref qualified base2
    class Derived, // Possibly cv-ref qualified derived
    class... Args  // Possibly cv-ref qualified args
>
struct overload_origin {
    using base1 = std::decay_t<Base1>;
    using base2 = std::decay_t<Base2>;
    using derived = std::decay_t<Derived>;
    using type = /* base1, base2, or derived * /
};
Run Code Online (Sandbox Code Playgroud)

并且当在使用call在前面的示例中代码的功能,将具有overload_origin::type指的base1,base2,base1,base1,derived用于在示例代码示出的五个电话.

如何用模板元编程实现这样的事情?

Oli*_*liv 3

derived您可以从和派生一个类base1。这样,所有operator()对 from的调用base1都会不明确:

struct derived_check: base1, derived {
    using base1::operator();
    using base2::operator();
};
// Main
int main(int argc, char* argv[]) {
    const derived_check d1;
    derived_check d2;
    derived_check d3;
    call(d1, 1);    // error:ambiguous
    call(d2, 1);    // OK
    call(d1, 1, 2); // error:ambiguous
    call(d2, 1, 2); // error:ambiguous

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用基本的检测技巧来创建您的检测类型特征。