4

私は C++ で関数型を試してきました。次のような関数へのポインタ型を意味するものではないことに注意してください。

typedef void (*voidFuncPtr)();

しかし、よりエキゾチックです:

typedef void (voidFunc)();

次のコードがコンパイルされるとは思っていませんでしたが、驚くべきことにコンパイルされました。

template<voidFunc func>
class funcClass
{
public:
    void call() { func(); };
};

void func()
{ }

void Test()
{
    funcClass<func> foobar;
    foobar.call();
}

ただし、funcClass に以下を追加しようとすると:

voidFuncPtr get() { return &func; }

エラーが発生しますAddress expression must be an lvalue or a function designator

ここでの私の最初の質問は次のとおりです。コンパイラは、func 型が実際にインスタンスを渡すことができるものであるふりをするために、どのような黒魔術を使用していますか? 参照のように扱っているだけですか?2 番目の質問は、それを呼び出すことさえできるのに、なぜそのアドレスを取得できないのかということです。また、これらの非ポインタ関数型は何と呼ばれますか? boost::function のためにそれらを発見しただけで、それらに関するドキュメントを見つけることができませんでした。

4

2 に答える 2

4

規格の§14.1.4は次のように述べています。

非型テンプレートパラメータは、次の(オプションでcv修飾された)型のいずれかを持つ必要があります。

—整数型または列挙型、

—オブジェクトへのポインタまたは関数へのポインタ、[これはあなたのものです]

—オブジェクトへの左辺値参照または関数への左辺値参照、

—メンバーへのポインタ、

— std::nullptr_t。

そして§14.1.6は言う

非型非参照テンプレートパラメータはprvalueです。割り当てられたり、その他の方法で値が変更されたりしてはなりません。非タイプの非参照テンプレートパラメータは、そのアドレスを取得できません。非型非参照template-parameterが参照の初期化子として使用される場合、一時が常に使用されます。

これで、表示されている2つの動作が説明されます。

これは(§14.3.2.1)funcと同じであることに注意してください。&func

[非型テンプレートパラメータは]静的な保存期間と外部または内部リンケージを持つオブジェクト、または関数テンプレートと関数テンプレートIDを含む外部または内部リンケージを持つ関数のアドレスを指定する定数式(5.19)です。非静的クラスメンバーを除き、&id-expressionとして表現されます(括弧は無視されます)。ただし、名前が関数または配列を参照する場合は&を 省略でき、対応するtemplate-parameterが参照の場合は省略できます。また...

つまり、これは単なる関数ポインタです。

于 2012-10-19T22:39:37.907 に答える
2

コードがaddress-of演算子なしでコンパイルされ、ポインター(関数およびメンバー関数を含む)が有効なテンプレート引数であることを考えると、コンパイラーvoidFuncは関数ポインター型、つまり型の減衰バージョンであると見なしているようです。このためのルールは、C++2003とC++2011の間で変更されていません。

于 2012-10-19T22:39:26.407 に答える