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

时间:2010-02-08 22:22:42

标签: c++

我正在尝试创建一个指向具有默认参数的成员函数的指针。当我通过这个函数指针调用时,我不想为默认参数指定一个参数。这是根据标准不允许的,但我从来没有发现任何标准禁止的东西,我不能以其他一致的方式做。到目前为止,我还没有办法做到这一点。

以下代码说明了我正在尝试解决的问题:

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

如果我不想为默认参数指定任何值,有关如何在符合C ++ 中执行此操作的任何想法吗?

编辑:稍微澄清一下限制。我不想在调用或任何typedef中指定任何默认参数。例如,我不想这样做:

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

...我也不想这样做:

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

4 个答案:

答案 0 :(得分:17)

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

另一方面,函数指针是运行时实体。它们在运行时初始化。在运行时默认参数根本不存在。在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_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_default相同),并将bp2设置为指向而是适配器。但是,目前C ++语言中没有这样的功能。而引入类似的东西需要比第一眼看上去更多的努力。

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

答案 1 :(得分:4)

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

struct MyFunctor {
    int operator() {
        return myobj.bar();
    }

    MyFunctor(MyObj &obj) : myobj(obj) {}
    MyObj &myobj;
};

然后:

MyFunctor myfunc(o);
myFunctor();

答案 2 :(得分:2)

鉴于约束条件,这是不可能的。您的选择是:

  1. 使用函数包装器。
  2. 使用Functors。
  3. 查看Boost的一些方便工具来简化这一过程。

答案 3 :(得分:0)

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

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
}

我们想编写一个辅助函数来捕获function1和function2,这样重复的代码A,B,C就不需要写两次了。

以下内容无法编译:

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 functionHelper (const Thing& thing, int a, int b, double c, void (Thing::*f)(int, double) const) {
    // Code A
    (thing.*f)(a,c);
    // Code B
//  (thing.*f)(b);  // Won't compile.  Too few arguments passed to (thing.*f), which expects (int, double).
    // Code C   
}

void function1 (const Thing& thing, int a, int b, double c) {
    functionHelper (thing, a, b, c, &Thing::foo);
}

void function2 (const Thing& thing, int a, int b, double c) {
    functionHelper (thing, a, b, c, &Thing::goo);
}

第一个解决方案(Thing :: foo和Thing :: goo的重载):

#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.

第二个解决方案(Wrap Thing :: foo和Thing :: goo into function objects):

#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.

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