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

时间:2018-03-02 07:54:35

标签: c++ overloading c++17 template-meta-programming functor

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

// 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;
}

结果输出为:

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)

说明了根据参数,所选的重载可以来自base1base2derived

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

这将具有以下形式:

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 * /
};

并且在前面示例代码中的call函数中使用时,会overload_origin::type引用base1base2base1,{{ 1}},base1用于示例代码中说明的五个调用。

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

1 个答案:

答案 0 :(得分:3)

您可以从derivedbase1派生一个班级。这样,来自operator()的所有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;
}

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