具有未解析的重载调用成员函数的c ++成员函数

时间:2016-11-21 16:45:30

标签: c++ function

我正在尝试使用成员函数创建一个对象,该函数调用另一个成员函数,如此

foo foo1 = new foo(1, 2);
foo1.print(printj);

我上课了:

class foo
{
   public:
     foo(int x, int y) {i = x; j = y;};
     void const print(void const f());
     void const printi();
     void const printj();
   private:
     int i;
     int j;
}

我的实现类似于:

void const foo::printi(){
   std::cout << i;
}
void const foo::printj(){
   std::cout << j;
}
void const foo::print(void const f()){
   f();
}

我收到了错误 [错误]没有匹配函数来调用'foo :: print()'

为什么会这样,我该如何解决?

3 个答案:

答案 0 :(得分:2)

你需要:

  1. 声明指向成员的函数参数:
  2.     void const print(void const (foo::*f)());
    1. 正确传递成员函数指针:
    2.     foo1.print(&foo::printj);
      1. 用实际实例调用它(成员函数调用需要一个实例):
      2.     void const foo::print(void const (foo::*f)()){
                (this->*f)();
            }

        或者,您可以将实例作为附加参数,或使用std::bindboost::bind将它们绑定在一起。

答案 1 :(得分:0)

这不是如何声明指向成员函数的指针的方式,你必须这样声明它:

const void (Foo::*ptrFUnc)(void) // or any number of parameters and type 

这个例子说明了如何:

#include <iostream>
using namespace std;

class Foo
{
    public:
        void print(const void(Foo::*Bar)()const)const;
        const void FooBar()const;
        const void Baz   ()const;
};

void Foo::print(const void(Foo::*Bar)()const)const
{
    (this->*Bar)();
}

const void Foo::FooBar()const
{
    cout << "FooBar()" << endl;
}

const void Foo::Baz()const
{
    cout << "Baz()" << endl;
}

int main()
{
    Foo theFoo;
    theFoo.print(theFoo.Baz);
    theFoo.print(theFoo.FooBar);

    return 0;
}

答案 2 :(得分:0)

注意:此答案针对一般情况场景和面向未来的情况,因此检查了接受具有不同数量的参数的成员函数以及将来修改函数的可能性。如果这不是问题,最简单的解决方案是手动指定指向成员的指针功能,如其他答案中所述。

简短摘要在底部。

还有两种方法可以手动声明函数的类型,如其他答案所示,都涉及模板:

  1. 手动声明。
  2. 第一种选择:使用模板专门指定指向成员的函数,同时明确指定类。
  3. 第二种选择:使用模板推导出指向成员的指针函数,没有明确的规范。
  4. 在所有三种情况下(手动声明和此处列出的两种备选方案),使用语法相同:

    foo1.print(&foo::printj);
    

    正如其他答案所示,手动声明它的语法如下:

    // #1: Explicit declaration.
    void const foo::print(void const (foo::* f)()) {
        (this->*f)();
    }
    

    我不会详细介绍这个,因为他们已经覆盖了它。但是,此选项确实存在这样的问题:如果要接受带有一个或多个参数的成员函数的指针,则需要手动重载它以适应这一点。

    void const foo::print(void const (foo::* f)());
    void const foo::print(void const (foo::* f)(int), int);
    // And so on...
    

    如果您不习惯模板,第一种选择看起来有点复杂,但实施起来相对简单。

    // 2a: Simplest implementation.
    template<typename Return, typename... ArgTypes>
    void const foo::print(Return (foo::* f)(ArgTypes...), ArgTypes... args) {
        (this->*f)(args...);
    }
    

    或者...

    // 2b: Works roughly the same way, but gives cleaner, more readable error messages.
    template<typename Return, typename... ArgTypes, typename... Args>
    void const foo::print(Return (foo::* f)(ArgTypes...), Args... args) {
        (this->*f)(args...);
    }
    

    它接受指向foo成员的任何指向成员的函数,无论返回和参数类型如何。如果函数接受参数,它还接受许多等于该函数的参数。

    请注意,两者之间的主要区别在于,如果没有为函数传递正确数量的参数,则第一个将给出错误,因为模板参数包不匹配导致无法推断ArgTypes...,而第二个将给出一个错误,即没有正确数量的参数来调用f()

    [在机械上,区别在于第一个在指针和参数列表中使用相同的模板参数包,这要求它在两个地方都相同(因此在{{1时)将错误检测为推导失败调用),而第二个使用单独的模板参数包(因此,当调用指向函数print()时,将错误检测为参数计数不匹配)。]

    第二种方法看起来更清晰,并提供更清晰的错误信息。

    f

    这可以很容易地修改为接受带有参数的成员函数,类似于第一种选择。

    template<typename MemberFunction>
    void const foo::print(MemberFunction f){
       (this->*f)();
    }
    

    如果为函数传递了错误数量的参数,它也会给出最干净的错误消息,因为在调用// 3: Take pointer-to-member-function and any function parameters as template parameters. template<typename MemberFunction, typename... Args> void const foo::print(MemberFunction f, Args... args){ (this->*f)(args...); } 而不是在重载解析或模板扣除时发生错误。如果需要,这使得最容易进行故障排除。

    因此,这给我们留下了三个选项,其中一个可以通过两种方式完成:

    f

    现在,机械地,所有三个选项都将接受指向成员的指针功能,并按预期工作。然而,有一些关键的区别:

    • 第一个需要更多的工作来更新和维护(你必须明确地重载它),但保证class foo { public: foo(int x, int y) {i = x; j = y; test = 42;}; // ----- // #1. void const print1(void const (foo::* f)()); // ----- // #2. template<typename Return, typename... ArgTypes> void const print2a(Return (foo::* f)(ArgTypes...), ArgTypes... args); template<typename Return, typename... ArgTypes, typename... Args> void const print2b(Return (foo::* f)(ArgTypes...), Args... args); // ----- // #3. template<typename MemberFunction, typename... Args> void const print3(MemberFunction f, Args... args); // ----- void const printi(); void const printj(); // For testing. void const printParams(int i, bool b, char c, double d); private: int i; int j; public: int test; }; void const foo::print1(void const (foo::* f)()) { (this->*f)(); } template<typename Return, typename... ArgTypes> void const foo::print2a(Return (foo::* f)(ArgTypes...), ArgTypes... args) { (this->*f)(args...); } template<typename Return, typename... ArgTypes, typename... Args> void const foo::print2b(Return (foo::* f)(ArgTypes...), Args... args) { (this->*f)(args...); } template<typename MemberFunction, typename... Args> void const foo::print3(MemberFunction f, Args... args) { (this->*f)(args...); } // ----- void const foo::printi(){ std::cout << i; } void const foo::printj(){ std::cout << j; } void const foo::printParams(int i, bool b, char c, double d) { std::cout << std::boolalpha; std::cout << i << ' ' << b << ' ' << c << ' ' << d << '\n'; std::cout << std::noboolalpha; } // ----- foo foo1(1, 2); 只接受你特别允许的指向成员函数的指针;如果您希望仅使用print1(),则不会使用void const (foo::*)()。如果传递了不正确的参数,则其错误消息将是最不实用的。
    • 第二个只接受指定类的指向成员函数的指针,但接受该类的任何指向成员的函数;这使得更新和维护更容易。如果传递了不正确的参数,它的错误消息将有所帮助,但通常是关于模板推导。在这两个版本中,void const (foo::*)(int)在传递错误数量的参数时会给出更清晰的错误消息。
    • 第三个采取任何措施,但如果使用不当,则会提供最干净,最有用的错误消息。这是因为当您致电print2b()而不是致电f时会产生错误。这和第二个选项一样易于更新和维护,并且可以根据它传递的指针类型执行调度。

    因此,为了证明错误信息的不同,让我们看看如果...发生了什么 [来自Clang,GCC和MSVC的错误消息。]
    [请注意,MSVC模板参数列表在使用可变参数模板时遇到问题,并且无法正确输出参数包。但是,函数的名称仍包含完整的模板参数列表。]

    • 如果传递一个没有参数的指向成员函数的指针:全部四个都正常工作。

      print3()
    • 如果传递了一个带参数的指针到成员函数,其参数:foo1.print1(&foo::printj); // Output: 2 foo1.print2a(&foo::printj); // Output: 2 foo1.print2b(&foo::printj); // Output: 2 foo1.print3(&foo::printj); // Output: 2 失败。

      print1()
    • 如果传递了一个指向成员的函数,该函数接受参数,参数数量错误:所有四个都失败。

      foo1.print1(&foo::printParams, 3, true, '&', 8.8);  // Error: Too many arguments.
      foo1.print2a(&foo::printParams, 3, true, '&', 8.8); // Output: 3 true & 8.8
      foo1.print2b(&foo::printParams, 3, true, '&', 8.8); // Output: 3 true & 8.8
      foo1.print3(&foo::printParams, 3, true, '&', 8.8);  // Output: 3 true & 8.8
      
    • 如果传递常规函数指针:全部四个都失败。

      foo1.print1(&foo::printParams, 42);  // Error: Too many arguments.
      foo1.print2a(&foo::printParams, 42); // Error: Can't deduce template parameters,
                                           //  ArgTypes... could be <int, bool, char, double> or <int>.
      foo1.print2b(&foo::printParams, 42); // Error: Not enough arguments to call f().
        // Note: Clang deduces template parameters as:
        //   <const void, int, bool, char, double, int>
        // Note: GCC deduces template parameters as:
        //   [with Return = const void; ArgTypes = {int, bool, char, double}; Args = {int}]
        // Note: MSVC deduces template parameters as:
        //   <const void,int,bool,char,double,int>
      foo1.print3(&foo::printParams, 42);  // Error: Not enough arguments to call f().
        // Note: Clang deduces template parameters as:
        //   <const void (foo::*)(int, bool, char, double), int>
        // Note: GCC deduces template parameters as:
        //   [with MemberFunction = const void (foo::*)(int, bool, char, double); Args = {int}]
        // Note: MSVC deduces template parameters as:
        //   <const void(__thiscall foo::* )(int,bool,char,double),int>
      
    • 如果为错误的类传递了指向成员函数的指针:全部四个都失败。

      void const bar() {}
      
      foo1.print1(&bar);  // Error: Can't convert void const (*)() to void const (foo::*)().
      foo1.print2a(&bar); // Error: Can't deduce template parameters, mismatched function pointers.
      foo1.print2b(&bar); // Error: Can't deduce template parameters, mismatched function pointers.
      foo1.print3(&bar);  // Error: void const (*)() isn't a pointer-to-member, can't be used with "->*".
      
    • 如果传递指向成员数据的指针:全部四个都失败。

      class oof {
        public:
          void const printj() {}
      };
      
      foo1.print1(&oof::printj);  // Error: Can't convert void const (oof::*)() to void const (foo::*)().
      foo1.print2a(&oof::printj); // Error: Can't deduce template parameters, mismatched foo* and oof*.
      foo1.print2b(&oof::printj); // Error: Can't deduce template parameters, mismatched foo* and oof*.
      foo1.print3(&oof::printj);  // Error: Can't use a void const (oof::*)() with a foo*.
      
    • 如果传递了常规指针:全部四个都失败。

      foo1.print1(&foo::test);  // Error: Can't convert int foo::* to void const (foo::*)().
      foo1.print2a(&foo::test); // Error: Can't deduce template parameters, mismatched
                                //  int foo::* and Return (foo::*)(ArgTypes...).
      foo1.print2b(&foo::test); // Error: Can't deduce template parameters, mismatched
                                //  int foo::* and Return (foo::*)(ArgTypes...).
      foo1.print3(&foo::test);  // Error: int foo::* can't be used as a function.
      
    • 如果传递一个整数值:全部四个都失败。

      foo1.print1(&foo);  // Error: Can't convert foo* to void const (foo::*)().
      foo1.print2a(&foo); // Error: Can't deduce template parameters, mismatched
                                //  foo* and Return (foo::*)(ArgTypes...).
      foo1.print2b(&foo); // Error: Can't deduce template parameters, mismatched
                                //  int foo::* and Return (foo::*)(ArgTypes...).
      foo1.print3(&foo);  // Error: foo* isn't a pointer-to-member, can't be used with "->*".
      

    等等......

    在这些选项中,foo1.print1(3); // Error: Can't convert int to void const (foo::*)(). foo1.print2a(3); // Error: Can't deduce template parameters, mismatched // int and Return (foo::*)(ArgTypes...). foo1.print2b(3); // Error: Can't deduce template parameters, mismatched // int and Return (foo::*)(ArgTypes...). foo1.print3(3); // Error: int isn't a pointer-to-member, can't be used with "->*". 在误用时始终提供最干净的错误消息,使其成为其他条件相同时的最佳选择。使用错误数量的参数调用时,print3()会提供更清晰的错误消息,否则会匹配print2b()

    要点:

    如上所述,有三种方法可以将指针指向成员函数:

    1. 手动声明,如其他答案中所述。

      print2a()
    2. 使用模板对其进行专门化,并采用它可能需要的任何参数。

      void const foo::print(void const (foo::* f)());
      

      或者...

      template<typename Return, typename... ArgTypes>
      void const foo::print(Return (foo::* f)(ArgTypes...), ArgTypes... args);
      
    3. 将该函数作为模板参数及其可能需要的任何参数。

      template<typename Return, typename... ArgTypes, typename... Args>
      void const foo::print(Return (foo::* f)(ArgTypes...), Args... args);
      
    4. 其中:

      • 第一个选项让您可以最大程度地控制允许哪些函数template<typename MemberFunction, typename... Args> void const foo::print(MemberFunction f, Args... args); ,但是要求您为要允许的每种类型的指向成员函数显式地重载它(例如{{ 1}}或print());这使它成为面向未来的最小证明,因为如果您向void (foo::*)()添加新功能并希望它能够使用它们,则需要更新该功能。
      • 相反,第二个和第三个选项是面向未来的,并且将采用任何指向成员的指针功能。但是,除非你付出额外的努力,否则他们不允许你限制你传递的成员函数类型。
      • 如果使用不正确,第三个选项将提供最干净的错误消息,这在进行故障排除时非常有用。相反,第一个选项通常会发出转换错误,第二个选项通常会发出一个&#34;不能推断出模板参数&#34;错误。如果使用带参数的成员函数调用,则在未提供这些参数时,第三个选项和第二个选项的第二个版本都将给出正确的错误;但是,第三个选项会发出更清晰,更容易阅读的错误。