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.