我正在尝试创建一个指向具有默认参数的成员函数的指针。当我通过这个函数指针调用时,我不想为默认参数指定一个参数。这是根据标准不允许的,但我从来没有发现任何标准禁止的东西,我不能以其他一致的方式做。到目前为止,我还没有办法做到这一点。
以下代码说明了我正在尝试解决的问题:
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);
答案 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)
鉴于约束条件,这是不可能的。您的选择是:
查看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.
您认为哪种解决方案更好?我认为第二个更优雅,但有更多的代码行(没有多态性我不能这样做。)