11

D::fooメンバーへのポインター関数を介して関数を呼び出す以下のメソッドは、エラーを生成します: 'f (...)' でメンバーへのポインター関数を使用.*または->*呼び出す必要があります .. もちろん、それはポインターへの呼び出し方法ではありませんメンバー関数。

正しい呼び出し方は(d.*f)(5);OR(p->*f)(5);

私の質問は、「左側にクラス オブジェクトがないクラスのメンバー関数を呼び出す方法はありますか?」です。thisクラス オブジェクト ( ) を通常の引数として渡すことはできますか?

私の考えでは、1 日の終わり (アセンブリ/バイナリ レベル) では、クラスのすべてのメンバー関数は通常の関数であり、n + 1 個の引数で動作する必要があります (+1 はthis)

以下の関数について話す場合D::foo、アセンブリ/バイナリ レベルでは、2 つの引数で動作する必要があります。

  1. クラス オブジェクト自体 ( と呼ばれるクラス D オブジェクトへのポインタthis)
  2. int.

それで、クラスオブジェクトで演算子D::fooを使用する代わりに、関数引数として渡されたクラスオブジェクトで呼び出す方法(またはハック)はありますか?. or -> or .* or ->*

サンプルコード:

#include <iostream>
using namespace std;

class D {
    public:
        void foo ( int a ) {
            cout << "D" << endl;
        }

        int data;
};


//typedef void  __cdecl ( D::*  Func)(int);
typedef void ( D::*  Func)(int);

int main ( void ) 
{
    D d;

    Func f = &D::foo;
    f(&d, 5);

    return 1;
 }

1つの方法は、ブーストバインドを使用することです

(boost:: bind (&D::foo, &d, 5)) ();

編集:「このプログラムの機能するバージョンを探しているわけではないことに注意してください。機能させる方法を知っています」

4

11 に答える 11

20

orを使用せずにメンバー関数を呼び出したいです.->? 本当に、本当に?まあいいよ...

Evil.h:

#ifdef __cplusplus
extern "C" {
#endif

struct MyStruct
{
#ifdef __cplusplus
    MyStruct();

    void method(int);
#endif
};

#ifdef __cplusplus
}
#endif

Evil.cc:

#include <iostream>

#include "evil.h"

MyStruct::MyStruct() { std::cout << "This is MyStruct's constructor" << std::endl; }

void MyStruct::method(int i) { std::cout << "You passed " << i << std::endl; }

Evil.c:

#include "evil.h"

int main()
{
    struct MyStruct my_struct;
    _ZN8MyStructC1Ev(&my_struct); /* MyStruct::MyStruct() */

    _ZN8MyStruct6methodEi(&my_struct, 3); /* MyStruct::method(int) */

    return 0;
}

これはたまたま Linux 上の gcc と g++ の組み合わせで機能しますが、言うまでもなくプラットフォーム ABI に依存しており、アンダースコアと大文字の形式の関数を呼び出す際に C89 標準に違反しています。ほとんどの場合、仮想関数では機能しません。私は試してみたいとは思いません。それはまた、私が今まで書いた中で最も邪悪なものかもしれません。それでも...


編集:OPを引用するには:

私の考えでは、1 日の終わり (アセンブリ/バイナリ レベル) では、クラスのすべてのメンバー関数は通常の関数であり、n + 1 個の引数で動作する必要があります (+1 はthis)

CFront 以降のすべてのコンパイラがこの方法を採用しているのは事実ですが、それは実装の詳細にすぎません。C++ 標準では、メンバー関数の実装方法を指定するのではなく、メンバー関数がどのように動作するかだけを指定することに苦労ています。

これは実装の詳細であるため、異なるプラットフォームでは異なる方法で実行されます。これは単なる名前マングリングにとどまりません。たとえば、Linux で使用される呼び出し規約thisは、最初の引数として渡されることを指定します。他の実装 (Borland、IIRC?)は最後の引数thisとして渡します。

そのため、メンバー関数を通常の関数として余分に扱いたい場合はthis、特定の ABI に制限する必要があります。この投稿は、それを行う方法の例を提供します (または、実際にそれを行うべきではない理由の例です!)

それで、を使用する代わりに、クラスオブジェクトを関数引数として渡して D::foo を呼び出す方法(またはハック)はありますか。または -> または .* または ->* クラスオブジェクトの演算子?

プラットフォーム固有の嫌な汚いハック...

于 2013-11-01T07:36:15.820 に答える
14

あなたが何を求めているのかよくわかりません。メンバーへのポインターを介した関数の呼び出しには、「人為的な制限」はありません。正しい構文を使用するだけです。

(d.*f)(5);  // for objects or references
(p->*f)(5); // for pointers

bind「魔法」はしません。実装のどこかで、まさにそれを行います。

一日の終わりには、2つの引数を取る関数です

いいえ、これは 1 つの引数を取り、オブジェクトに対して呼び出されるメンバー関数です。概念的には 2 つの引数を取る関数に似ていますが、大きな違いが 1 つあります。メンバー関数は、ランタイム ディスパッチ メカニズムを含む仮想関数にすることができます。

于 2013-10-09T10:34:35.790 に答える
4

私が見る唯一の方法は、型定義された関数ポインターを関数のように動作するクラスに置き換えることです。

このようなものをお探しですか?

次のコードでは、マクロを使用してテンプレートを typedef し、説明した関数のように動作します。

#include <iostream>
using namespace std;

class D {
    public:
        void foo ( int a ) {
            cout << "D" << endl;
        }

        int data;
};

template<class Object, class Param, class Function>
class FunctionPointerHelper                                           
{                                                    
private:                                             
    Function m_function;                             
public:                                              
    FunctionPointerHelper(Function function) :                        
        m_function(function)                         
    {                                                
    }                                                
    void operator=(Function function)                
    {                                                
        m_function = function;                       
    }                                                
    void operator()(Object& object, Param param) const      
    {                                                
        (object.*m_function)(param);                 
    }                                                
};

#define TYPEDEF_NICE_FUNCTION(RESULT, CLASS, NEW_TYPENAME, PARAM) \
typedef RESULT ( CLASS::* NEW_TYPENAME_INTERMEDIATE)(PARAM) ; \
typedef FunctionPointerHelper<CLASS, PARAM, NEW_TYPENAME_INTERMEDIATE> NEW_TYPENAME;

TYPEDEF_NICE_FUNCTION(void, D, Func, int)

int main ( void ) 
{
    D d;

    Func f = &D::foo;
    f(d, 5);

    return 1;
 }
于 2013-11-06T17:41:22.203 に答える
3

左側にクラス オブジェクトがないクラスのメンバー関数を呼び出す方法はありますか?

組み立てなしの2つの方法

#include <iostream>
using namespace std;

class D {
    public:
        void foo ( int a ) {
            cout << "D" << endl;
        }

    static void foo(D& d, int a)
    {
        d.foo(a);
    }

        int data;
};

void foo(D& d, int a)
{
    d.foo(a);
}

int main ( void ) 
{
    D d;

    D::foo(d, 5);
    foo(d, 5);

    return 0;
}
于 2013-11-06T18:15:55.253 に答える
2

しかし、C++ によって課されるこの人為的な制限を回避する方法が必要です。

「人為的な制限」とはどういう意味ですか? 言語が定義する構文にすぎません。どうしたの?の「魔法」は、演算子を内部的にbind()使用して関数を呼び出します。->*

于 2013-10-09T10:35:17.763 に答える
2

はい、 Callable オブジェクトに適用されるINVOKEと呼ばれる C++11 標準の抽象化があります。Pointer-to-member-function (PTMF) オブジェクトは Callable でありthis、最初の (通常の) 引数として渡されます。

std::invoke提案されていますが、機能はありません。Callable オブジェクトからファンクターを取得するstd::bindには、Boost.bind の派生物である を使用できます。

これはうまくいきます:

int main ( void ) 
{
    D d;

    auto f = std::bind( &D::foo, _1, _2 );
    f(&d, 5);
 }

http://ideone.com/VPWV5U

于 2013-11-07T04:05:48.450 に答える
1

はい、十分なゆがみがあれば「新しい」メンバー関数の構文を回避できるのは事実ですが、私は尋ねなければなりません:なぜそうするのですか? この構文は、メンバー関数が何らかの方法で周囲のオブジェクトから呼び出されるという理解を促進します。仮想関数の存在を考えると、これは実際に当てはまります (そしてそうでなければなりません)。また、仮想関数の呼び出し (および仮想継承のサポート) に必要な this ポインターの変更も自動化します。

これを行う方法については、FastDelegate ライブラリの実装を参照してください。実装には、コンパイラのメンバー関数ポインター (いくつかの種類がある可能性があります) のバイナリ構造を知る必要があります。それがあなたが探している「ハック」です。FastDelegate のデリゲート (つまり、クロージャ) は、コール ポイントで 2 つの命令に変わります: 計算された「this」値を適切な場所に (呼び出し規約に基づいて) プルし、実際の関数のエントリ アドレスに間接的にジャンプします。

これは最終的に次のようになります。

fastdelegate::FastDelegate0<> functionObject;
SomeClass someInstance;

//fill in object and function information in function object
functionObject.bind(&someInstance,&SomeClass::someFunction);

//call function via object.  Equivalent to: someInstance->someFunction();
functionObject();

これは、boost::bind とその仲間が行っていることと非常に似ていますが、一般的には高速です (ただし、明らかに移植性が低くなります)。

ここで使用されているテンプレートと演算子のオーバーロードの下には、&someInstance を SomeClass::someInstance が必要とする this ポインターに変更する方法を計算する数学 (bind 関数内) があります。また、基になる関数の実際のアドレスを見つけて、後で使用できるように両方の値を記録します。呼び出しが行われると、コンパイラは->*. しかし、本当に演算子に依存することさえ避けたい場合->*は、その時点でいくつかの型キャストを実行して、ポインターを "__thiscall" 関数ポインターに変換できます (Windows を使用していると仮定します)。

__thiscall 呼び出し規約はメンバー関数で使用され、可変引数を使用しない C++ メンバー関数で使用される既定の呼び出し規約です。__thiscall の下では、呼び出し先がスタックを消去しますが、これは vararg 関数では不可能です。x86 アーキテクチャでは、引数はスタック上で右から左にプッシュされます。このポインタはレジスタ ECX を介して渡されますが、スタック上では渡されません。

では、someInstance->someFunction() の何が気に入らなかったのでしょうか?

于 2013-11-02T00:47:36.877 に答える
0

基準は、この件に関して非常に明確で、まったく曖昧ではありません。セクション 5.2.2 (C++11) の最初の文は次のように述べています。

関数呼び出しには、通常の関数呼び出しとメンバー関数呼び出しの 2 種類があります。

つまり、それらは異なるものであり、混同することはできません。

于 2013-11-04T08:02:43.133 に答える
0

左側にクラス オブジェクトがないクラスのメンバー関数を呼び出す方法はありますか?

...

それで、クラスオブジェクトでorまたはor演算子D::fooを使用する代わりに、関数引数として渡されたクラスオブジェクトで呼び出す方法 (またはハック) はありますか?.->.*->*

要するに、いいえ。static呼び出しの左側で何らかの方法でオブジェクトを指定せずに、非クラス メンバ関数を呼び出すことはできません。クラス (静的関数の場合) またはオブジェクト (非静的関数の場合) が指定されていない場合、コンパイラは、呼び出しようとしている関数を知る方法がありません。クラスまたはオブジェクト内にあるため、現在のスコープ外です。コードを「ハック」して、例のようmain()に書くことができる方法があります。他の回答のいくつかは、これらの種類のハックの例を示しています。

------------静的関数の使用-----------------

クラス内の関数は、クラス オブジェクトを指定せずにクラス外で呼び出すことができます。これを行う方法はtypedef、クラス関数と一致するシグネチャを持つ関数ポインターの for を作成し、そのようなポインターを作成して、クラス関数のアドレスを割り当てることです。その関数は、左側のクラスまたはオブジェクトを指定する必要なく呼び出すことができます。

関数は でなければなりませstatic。ISO C++ では、バインドされたメンバ関数のアドレスを取得してその関数へのポインタを作成することはできません。つまりstatic、クラス オブジェクトから非メンバ関数へのポインタを作成することはできません。ISO 標準に準拠していないコンパイラを見つけることができるかもしれませんが、それに関するガイダンスはありません。

this非クラス メンバー関数へのパラメーターstaticは暗黙のパラメーターであり、常に暗黙のパラメーターです。手動で指定することはできませんが、クラスのオブジェクトへのポインターを渡すことでエミュレートできます。

したがって、コード例の場合:

  • void D::foo(int)である必要がstaticあり、パラメータが追加されている必要がありD*ます: class D { static void D::foo(D*, int); };
  • typedef void (D::* Func)(int);以前の変更に対応するように変更して、次を削除する必要がありますD::typedef void (*Func)(D*, int);
于 2013-11-02T00:39:16.030 に答える