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

问题描述 投票:6回答:1

考虑以下代码,其中函子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将引用base1base2base1base1derived作为示例代码中说明的五个调用。

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

c++ overloading c++17 template-meta-programming functor
1个回答
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;
}

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

© www.soinside.com 2019 - 2024. All rights reserved.