【问题标题】:Trick for overloads重载技巧
【发布时间】:2015-08-06 04:54:41
【问题描述】:

考虑这段代码:

#include <iostream>

class A {
public:
    template <typename... Args> void execute(Args&&... args) {foo(std::forward<Args>(args)...);}
    template <typename... Args> void doIt(Args&&... args) {bar(std::forward<Args>(args)...);}
private:
    void foo() {}
    void foo(int) {}
    void foo(int, char) {}
    void foo(bool, int, double) {}
    void bar() {foo();}  // *** Added
    void bar(int) {}
    void bar(int num, char c) {foo(num, c);}  // *** Added
    void bar(bool, int, double) {}
};

int main() {
    A a;
    a.doIt();
    a.doIt(5,'t');
}

A::doIt 将具有与A::execute 相同的重载类型,并且将对其中一些使用bar 的重载(其中 bar 的重载是唯一的),并为其他使用foo 的重载。为此,我简单地添加了void bar() {foo();} 和void bar(int num, char c) {foo(num, c);}。这不是那么痛苦,但假设有很多这样的转发要做。并在需要 doIt 的新重载时考虑维护问题,并为它们定义新的 foo 重载(容易忘记转发的东西)。

有没有办法删除这些额外的bar 重载(只需转发到foo),而是修改当前定义的doIt(Args&amp;&amp;... args) 函数,以便在不存在此类条形重载的情况下调用@987654333 @的重载而不是?所以换句话说,删除我添加的两个bar 重载,由于doIt 的新定义,main() 仍然可以按预期编译。

【问题讨论】:

  • 可能是部分专业化。
  • 您的意思是使用 std::enable if、std::declval 等?如何处理所有参数类型?
  • 这样的,是的。 SFINAE。 “如何处理所有的参数类型?” 你可能已经在做转发了。我现在不知道有现成的解决方案可以解决您的问题,但这是我会尝试查找的目录。
  • 您可以将A 拆分为A_bar 和A_foo,将所有bars 重命名为foo 并让A_foo 继承自A_bar。
  • 如果你想走那条路,那么在A_bar中添加using A_foo::foo;,否则A_foo中foo的声明被隐藏。

标签: c++ templates overloading


【解决方案1】:

SFINAE 关于bar(std::forward&lt;Args&gt;(args)...) 的格式良好。

class A {
private:
    void foo();
    void foo(int);
    void foo(int, char);
    void foo(bool, int, double);
    void bar(int);
    void bar(bool, int, double);

    template <typename... Args> 
    auto doIt_impl(int, Args&&... args) -> decltype(bar(std::forward<Args>(args)...)){
        bar(std::forward<Args>(args)...);
    }
    template <typename... Args> 
    auto doIt_impl(long, Args&&... args) -> void {
        foo(std::forward<Args>(args)...);
    }

public:
    template <typename... Args> void doIt(Args&&... args) {
        doIt_impl(0, std::forward<Args>(args)...);
    }
};

虚拟的第一个参数确保doIt_impl 的bar-calling 重载在可行的情况下是首选的。另外,需要注意的是尾随返回类型不会进行类范围查找,因此doIt_impl 的声明必须在bars 的声明之后。

Demo.

【讨论】:

    【解决方案2】:

    感谢 nwp 的提示(虽然我不确定这是否是他的意思)和 T.C. 的调试提示,我有了这个替代解决方案。但是 T.C. 的解决方案要好得多。

    #include <iostream>
    
    class A_Foo {
    protected:
        virtual void foo() {std::cout << "foo().\n";}
        virtual void foo(int) {std::cout << "foo(int).\n";}
        virtual void foo(int, char) {std::cout << "foo(int, char).\n";}
        virtual void foo(bool, int, double) {std::cout << "foo(bool, int, double).\n";}
    };
    
    class A_Bar : private A_Foo {
    public:
        using A_Foo::foo;  // This is needed.  Else all the declarations of foo in A_Foo are hidden.
        virtual void foo(int) override {std::cout << "bar(int).\n";}
        virtual void foo(bool, int, double) override {std::cout << "bar(bool, int, double).\n";}
    };
    
    class A : private A_Bar {
    public:
        template <typename... Args> void execute(Args&&... args) {
            A_Foo::foo(std::forward<Args>(args)...);
        }
        template <typename... Args> void doIt(Args&&... args) {
            A_Bar::foo(std::forward<Args>(args)...);
        }
    };
    
    int main() {
        A a;
        a.doIt();  // foo().
        a.doIt(5);  // bar(int).
        a.doIt(5,'t');  // foo(int, char).
        a.doIt(true,5,1.8);  // bar(bool, int, double).
    }
    

    【讨论】:

    • 你不需要virtual - 反正你所有的电话都是非虚拟的。
    猜你喜欢
    • 1970-01-01
    • 2011-03-15
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    • 2010-11-17
    • 1970-01-01
    • 1970-01-01
    • 2011-08-30
    相关资源
    最近更新 更多