簡體   English   中英

具有無法解決的重載調用成員函數的C ++成員函數

[英]c++ member functions with unresolved overload calling member functions

我正在嘗試使用一個成員函數制作一個對象,該成員函數像這樣調用另一個成員函數

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

我收到[Error]沒有匹配函數來調用'foo :: print()'的錯誤

為什么會這樣,我該如何解決?

你需要:

  1. 這樣聲明指針成員函數參數:
    void const print(void const (foo::*f)());
  1. 正確傳遞成員函數指針:
    foo1.print(&foo::printj);
  1. 用實際實例調用它(成員函數調用需要一個實例):
    void const foo::print(void const (foo::*f)()){
        (this->*f)();
    }

或者,您可以使實例成為附加參數,或使用std::bindboost::bind將它們綁定在一起。

這不是如何聲明指向成員函數的指針的方法,您必須以這種方式聲明它:

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

注意:此答案針對一般情況和面向未來的情況,因此研究了接受帶有不同數量參數的成員函數以及將來修改該函數的可能性。 如果這不是問題,則最簡單的解決方案是手動指定指向成員函數的指針,如其他答案所述。

簡短摘要在底部。


手動聲明函數的類型還有兩種選擇,如其他答案所示,它們都涉及模板:

  1. 手動聲明。
  2. 第一種選擇:使用模板來專門化指向成員函數的指針,同時顯式指定類。
  3. 第二種選擇:使用模板來推斷沒有成員明確說明的成員函數指針。

在所有三種情況下(手動聲明以及此處列出的兩個替代方案),用法語法都是相同的:

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()

[機械上的區別是,第一個在指針和參數列表中使用相同的模板參數包,這要求兩個位置都相同(並因此在調用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而不是在調用print3() 這與第二個選項一樣容易更新和維護,並提供了根據傳遞的指針類型執行分派的可能性。

因此,為演示錯誤消息中的差異,讓我們看看如果...
[從Clang,GCC和MSVC解釋的錯誤消息。]
[請注意,MSVC模板參數列表在使用可變參數模板時會遇到問題,並且無法正確輸出參數包。 但是,該函數的名稱仍包含完整的模板參數列表。]

  • 如果傳遞了不帶參數的指向成員函數的指針:四個都正常工作。

     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()它會提供更清晰的錯誤消息,但與print2a()匹配。


摘要:

如上所述,有三種方法可以獲取指向成員函數的指針:

  1. 手動聲明它,如其他答案所述。

     void const foo::print(void const (foo::* f)()); 
  2. 使用模板對其進行專門化,並獲取可能需要的任何參數。

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

    要么...

     template<typename Return, typename... ArgTypes, typename... Args> void const foo::print(Return (foo::* f)(ArgTypes...), Args... args); 
  3. 將函數作為模板參數以及它可能需要的任何參數。

     template<typename MemberFunction, typename... Args> void const foo::print(MemberFunction f, Args... args); 

這些:

  • 第一個選項使您可以最大程度地控制允許使用print()函數,但要求您為要允許的每種類型的指向成員函數的指針顯式重載它(例如void (foo::*)()int (foo::*)(int, int) ); 這使它成為最不適合未來的方法,因為如果您向foo添加新函數並希望它接受它們,則需要更新該函數。
  • 相反,第二個和第三個選項是面向未來的,它將采用任何指向成員函數的指針。 但是,除非您付出額外的努力,否則它們不允許您限制傳遞的成員函數類型。
  • 如果使用不當,第三個選項將給出最清晰的錯誤消息,這在進行故障排除時很有用。 相反,第一個選項通常會產生轉換錯誤,第二個選項通常會產生“無法推斷模板參數”錯誤。 如果使用帶有參數的成員函數調用,則在不提供這些參數的情況下,第三個選項和第二個選項的第二個版本都會給出正確的錯誤; 但是,第三個選項將發出更清晰,更易於閱讀的錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM