Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/124.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 具有默认参数的成员函数指针_C++ - Fatal编程技术网

C++ 具有默认参数的成员函数指针

C++ 具有默认参数的成员函数指针,c++,C++,我试图创建一个指向具有默认参数的成员函数的指针。当我通过这个函数指针调用时,我不想为默认参数指定参数。根据标准,这是不允许的,但我以前从未发现任何标准不允许的,我不能以其他符合性的方式做的事情。到目前为止,我还没有找到这样做的方法 下面是我试图解决的问题的代码: class MyObj { public: int foo(const char* val) { return 1; } int bar(int val = 42) { return 2; } }; int main() { M

我试图创建一个指向具有默认参数的成员函数的指针。当我通过这个函数指针调用时,我不想为默认参数指定参数。根据标准,这是不允许的,但我以前从未发现任何标准不允许的,我不能以其他符合性的方式做的事情。到目前为止,我还没有找到这样做的方法

下面是我试图解决的问题的代码:

class MyObj
{
public:
 int foo(const char* val) { return 1; }
 int bar(int val = 42) { return 2; }
};

int main()
{
 MyObj o;

 typedef int(MyObj::*fooptr)(const char*);
 fooptr fp = &MyObj::foo;
 int r1 = (o.*fp)("Hello, foo.");

 typedef int(MyObj::*barptr)(int);
 barptr bp1 = &MyObj::bar;
 int r2 = (o.*bp1)(); // <-- ERROR: too few arguments for call

 typedef int (MyObj::*barptr2)();
 barptr2 bp2 = &MyObj::bar; // <-- ERROR: Can't convert from int(MyObj::*)(int) to int(MyObj::*)(void)
 int r3 = (o.*bp2)();
    return 0;
}
…我也不想这样做:

typedef int(MyObj::*barptr)(int = 5);
typedef int(MyObj::*barptr)(int);
...
(o.barptr)(5);

考虑到这些限制,这是不可能的。你的选择是:

  • 使用函数包装器
  • 使用函子

  • 查看Boost以获得一些简化此过程的便捷工具。

    当然,您可以创建函子而不是函数指针

    struct MyFunctor {
        int operator() {
            return myobj.bar();
        }
    
        MyFunctor(MyObj &obj) : myobj(obj) {}
        MyObj &myobj;
    };
    
    然后:


    在您的示例中,期望函数指针以您期望的方式工作是相当奇怪的。“默认参数”是一个纯粹的编译时概念,它是语法糖的一种形式。尽管默认参数是在函数声明或定义中指定的,但它们实际上与函数本身无关。实际上,默认参数在调用时被替换,也就是说,它们是在调用方的上下文中处理的。从函数的角度来看,用户提供的显式参数和编译器隐式提供的默认参数之间没有区别

    另一方面,函数指针是运行时实体。它们在运行时初始化。在运行时,默认参数根本不存在。C++中没有“运行时默认参数”这样的概念。 某些编译器允许您在函数指针声明中指定默认参数,如

    void foo(int);
    
    int main() {
       void (*pfoo)(int = 42) = foo;
       pfoo(); // same as 'pfoo(42)'
    }
    

    但是这不是标准C++,这并不是你要找的,因为你希望“默认参数”值在运行时根据指针指向的函数而改变。 只要您想坚持使用真正的函数指针(与函数对象相反,也称为函子),立即的解决方法就是使用不同的名称提供函数的无参数版本,如中所示

    class MyObj 
    { 
    public: 
      ...
      int bar(int val = 42) { return 2; } 
      int bar_default() { return bar(); }
    }; 
    
    int main() 
    { 
      MyObj o; 
    
      typedef int (MyObj::*barptr2)(); 
      barptr2 bp2 = &MyObj::bar_default;
      int r3 = (o.*bp2)(); 
      return 0; 
    } 
    
    当然,这远非优雅

    实际上,有人可能会争辩说,作为一种语言特性,我在上面使用
    bar\u default
    所做的工作可能是由编译器隐式完成的。例如,给定类定义

    class MyObj 
    { 
    public: 
      ...
      int bar(int val = 42) { return 2; } 
      ...
    }; 
    
    人们可能期望编译器允许以下操作

    int main() 
    { 
      MyObj o; 
    
      typedef int (MyObj::*barptr2)(); 
      barptr2 bp2 = &MyObj::bar;
      int r3 = (o.*bp2)(); 
      return 0; 
    } 
    
    指针初始化实际上会强制编译器为
    MyObj::bar
    隐式生成一个“适配器”函数(与上一个示例中的
    bar\u default
    相同),并将
    bp2
    设置为指向该适配器。但是,目前C++语言中没有这样的特性。而要推出这样的产品,需要付出比乍一看更多的努力


    还要注意,在最后两个示例中,指针类型是
    int(MyObj::*)()
    ,这与
    int(MyObj::*)(int)
    不同。这实际上是一个问题(因为您在示例中尝试了这两种方法):您希望它如何工作?使用
    int(MyObj::*)()
    指针?或者使用
    int(MyObj::*)(int)
    指针?

    任务:假设您有以下内容:

    class Thing {
        public:
            void foo (int, double = 3.14) const {std::cout << "Thing::foo(int, double = 3.14) called.\n";}
            void goo (int, double = 1.5) const {std::cout << "Thing::goo(int, double = 1.5) called.\n";}
    };
    
    void function1 (const Thing& thing, int a, int b, double c) {
        // Code A
        thing.foo(a,c);
        // Code B
        thing.foo(b);
        // Code C
    }
    
    void function2 (const Thing& thing, int a, int b, double c) {
        // Code A
        thing.goo(a,c);
        // Code B
        thing.goo(b);
        // Code C
    }
    
    输出:

    Thing::foo(int, double = 3.14) called.
    Thing::foo_default(int) called.
    Thing::foo(int, double = 3.14) called.
    
    Thing::goo(int, double = 1.5) called.
    Thing::goo_default(int) called.
    Thing::goo(int, double = 1.5) called.
    
    Thing::foo(int, double = 3.14) called.
    Thing::foo(int, double = 3.14) called.
    Thing::FooOrGoo object destroyed.
    
    Thing::goo(int, double = 1.5) called.
    Thing::goo(int, double = 1.5) called.
    Thing::FooOrGoo object destroyed.
    
    第二种解决方案(将
    Thing::foo
    Thing::goo
    包装到函数对象中):


    您认为哪种解决方案更好?我认为第二个更优雅,但代码行更多(如果没有多态性,我就做不到)。

    只需调用o.foo(“blah”)和o.bar()?@Goz:请再次阅读这篇文章。我正在尝试使用函数指针。默认参数起作用的原因是编译器可以看到它。只使用函数指针,任何像默认值这样的信息都会完全消失。你必须指定它,或者做一个包装器。@GMan:对,这正是我面临的问题。我认为可能有一个我没有想到的新解决方案,因为正如我所说,我从来没有发现在标准C++中我不能做的任何事情。正如Andrey所说,函数指针与它们绑定到的函数的关系为零。Re:“Default argument”是一个纯粹的编译时概念——没错,但我希望编译器知道我通过指针调用的同一翻译单元中的默认值,有办法让它说出信息。@John:那么编译器怎么知道函数指针指向什么呢?直到运行时才绑定它;C++编译器不必做静态分析。约翰:我认为,在任何情况下,当函数指针的使用都是合法的时,这将是非常昂贵的。实现这一点的唯一合理方法是将所有这些信息填充到指针本身,即使用“胖”指针。C++中没有这样做。如果您需要一个“胖”指针,请改用函子。@AndreyT:我怀疑我对何时使用函数指针的估计比您的估计要宽松。:)@John Dibling:在您的例子中,编译器很容易弄清楚参数应该是什么,因为指针绑定对编译器来说是显而易见的。但通常,当绑定非常明显时,根本不需要指针(如您的人工示例中所示)。一旦绑定成为真正的运行时,编译器就不可能正确地确定默认参数值(除非它将这些值填充到指针本身中,或者使用我在回答末尾描述的方法).我认为Boost中没有任何东西可以阻止他以某种方式指定默认参数的值。检查此相关线程:使用此函子与直接调用
    o.bar()
    有何不同?@Manuel:使用该函子不会太琐碎,以致于创建一个函子,
    #include <iostream>
    
    class Thing {
        public:
            void foo (int, double = 3.14) const {std::cout << "Thing::foo(int, double = 3.14) called.\n";}
            void foo_default (int a) const {
                    std::cout << "Thing::foo_default(int) called.\n";
                    foo(a);
            }
            void goo (int, double = 1.5) const {std::cout << "Thing::goo(int, double = 1.5) called.\n";}
            void goo_default (int a) const {
                    std::cout << "Thing::goo_default(int) called.\n";
                    goo(a);
            }
    };
    
    void functionHelper (const Thing& thing, int a, int b, double c,
            void (Thing::*f)(int, double) const, void (Thing::*g)(int) const) {
        // Code A
        (thing.*f)(a,c);
        // Code B
        (thing.*g)(b);  // This will compile now, since (thing.*g) expects int only as argument.
        // Code C   
    }
    
    void function1 (const Thing& thing, int a, int b, double c) {
        functionHelper (thing, a, b, c, &Thing::foo, &Thing::foo_default);
    }
    
    void function2 (const Thing& thing, int a, int b, double c) {
        functionHelper (thing, a, b, c, &Thing::goo, &Thing::goo_default);
    }
    
    int main() {
        Thing thing;
        function1 (thing, 2, 5, 1.8);
        std::cout << '\n';
        function2 (thing, 2, 5, 1.8);
    }
    
    Thing::foo(int, double = 3.14) called.
    Thing::foo_default(int) called.
    Thing::foo(int, double = 3.14) called.
    
    Thing::goo(int, double = 1.5) called.
    Thing::goo_default(int) called.
    Thing::goo(int, double = 1.5) called.
    
    #include <iostream>
    #include <memory>
    
    class Thing {
        public:
            void foo (int, double = 3.14) const {std::cout << "Thing::foo(int, double = 3.14) called.\n";}
            void goo (int, double = 1.5) const {std::cout << "Thing::goo(int, double = 1.5) called.\n";}
            
            class FooOrGoo {
                public: 
                    void operator()(const Thing& thing, int a) const {helper1 (thing, a);}
                    void operator()(const Thing& thing, int a, double b) {helper2 (thing, a, b);}
                    virtual ~FooOrGoo() {std::cout << "Thing::FooOrGoo object destroyed.\n";}
                private:
                    virtual void helper1 (const Thing& thing, int a) const = 0;
                    virtual void helper2 (const Thing& thing, int a, double b) const = 0;
            };
    
            class Foo : public FooOrGoo {
                virtual void helper1 (const Thing& thing, int a) const override {thing.foo(a);}
                virtual void helper2 (const Thing& thing, int a, double b) const override {thing.foo(a, b);}
            };
    
            class Goo : public FooOrGoo {
                virtual void helper1 (const Thing& thing, int a) const override {thing.goo(a);}
                virtual void helper2 (const Thing& thing, int a, double b) const override {thing.goo(a, b);}
            };
    };
    
    void functionHelper (const Thing& thing, int a, int b, double c, std::unique_ptr<Thing::FooOrGoo> f) {
        // Code A
        (*f)(thing, a,c);
        // Code B
        (*f)(thing, b);
        // Code C   
    }
    
    void function1 (const Thing& thing, int a, int b, double c) {
        functionHelper (thing, a, b, c, std::unique_ptr<Thing::Foo>(new Thing::Foo));  // 'std::make_unique<Thing::Foo>());' is not supported by GCC 4.8.1.
    }
    
    void function2 (const Thing& thing, int a, int b, double c) {
        functionHelper (thing, a, b, c, std::unique_ptr<Thing::Goo>(new Thing::Goo));  // 'std::make_unique<Thing::Goo>());' is not supported by GCC 4.8.1.
    }
    
    int main() {
        Thing thing;
        function1 (thing, 2, 5, 1.8);
        std::cout << '\n';
        function2 (thing, 2, 5, 1.8);
    }
    
    Thing::foo(int, double = 3.14) called.
    Thing::foo(int, double = 3.14) called.
    Thing::FooOrGoo object destroyed.
    
    Thing::goo(int, double = 1.5) called.
    Thing::goo(int, double = 1.5) called.
    Thing::FooOrGoo object destroyed.