3

関数ポインターを引数として取り、その関数を独自の引数で呼び出す関数があります。

typedef int (*my_func_ptr)( int );

int foo( my_func_ptr f ) {
    static int i = 0;
    return i = f( i );
}

foo場合によっては、結果を吐き出すために整数入力以上のものに依存する関数を渡す必要があります。

int add_strlen( int i, const char* s ) {
    return i + strlen( s );
}

上記のコードを作り直して を使用することもできますがstd::functionstd::bindこれらの関数はコンパイル時に作成する方が望ましいので、テンプレートを使用しています。

template<const char* S>
int add_strlen( int i ) {
    return i + strlen( S );
}

/**
 * Usage:
 * char bar[] = "bar";
 * foo( add_strlen<bar> );
 */

テンプレート引数としてポインターを使用すると、私の問題が発生します。テンプレート引数として任意の型の定数データへのポインターを使用するときはいつでも、渡される引数がその型の非定数配列として宣言されている場合にのみコンパイルできます。

char char_array[]             = "works";
const char const_char_array[] = "error";
char *char_ptr                = "error";
const char *const_char_ptr    = "error";

関連する Clang (ver. 3.0-6) のエラー (char_ptrとのエラーconst_char_ptrは同じです):

func_ptr.cpp:29:9: error: no matching function for call to 'foo'
        foo( add_strlen<const_char_array> );
        ^~~
func_ptr.cpp:6:5: note: candidate function not viable: no overload of 'add_strlen' matching 'my_func_ptr' (aka 'int (*)(int)') for 1st argument
int foo( my_func_ptr f )

誰かが私にこれがなぜなのか説明できますか? 私の見方では、テンプレート パラメータSは typeconst char*であると予想されます。これは、他の状況では、任意の const または非 const ポインタまたは型の配列を渡して、charそれが機能することを期待できることを意味します。配列を として宣言できるようにしconstたいと考えています。なぜなら、配列が実行時に変更されることを意図していることを暗示したくないからです。配列を const に保ち、テンプレート引数として使用する方法はありますか?

編集:いくつかの助け(およびより良いエラーを含む新しいバージョンのClang)のおかげで、内部リンケージを持つテンプレート引数を提供することが問題の一部であると判断できました。上記の変数をexternとして宣言することで、add_strlen<const_char_array>エラーなく使用できます。簡単なテスト ケースも作成しました。以下に含まれています。


#include <cstring>

typedef int (*my_func_ptr)( int );

int foo( my_func_ptr f ) {
    static int i = 0;
    return i = f( i );
}

template<const char* S>
int add_strlen( int i ) {
    return i + strlen( S );
}

extern char char_array[];
extern const char const_char_array[];
extern char *char_ptr;
extern const char *const_char_ptr;

char char_array[] = "foo";
const char const_char_array[] = "bar";
// assigning to string literal is deprecated
char *char_ptr = char_array;
const char *const_char_ptr = "baz";

int main(int argc, const char *argv[])
{
    foo( add_strlen<char_array> ); // works
    foo( add_strlen<const_char_array> ); // works
    //foo( add_strlen<char_ptr> ); // doesn't work
    //foo( add_strlen<const_char_ptr> ); // doesn't work
    return 0;
}
4

1 に答える 1

1

エラーは、あなたが何であるか、および非型テンプレート パラメーターとして使用することが許可されていないものに関連しているようです。

非型テンプレート パラメーターの構文は、次のいずれかの型の宣言と同じです。

  • 整数または列挙
  • オブジェクトへのポインタまたは関数へのポインタ
  • オブジェクトへの参照または関数への参照
  • メンバーへのポインター

char_array[]渡されたときにandが機能する理由const_char_array[]は、コンパイル時に定数であり、実行中にプログラムの下で決して変更されないためです。整数型は渡すことができますが、整数型へのポインターは渡すことができません。

テンプレートはconst char *akaの型を想定const char[x]していますが、変更されないものも想定しているため、ポインタが指している場所は変更されない可能性があります。コンパイラ時const_char_arrayに渡されると、char[6](「エラー」) が渡されます。場所は変わりませんし、中身も変わりません。ただし、 を渡すconst_char_ptrと が取得されconst char *、ポインター自体は変更されない可能性がありますが、ポインターが指す場所が変更される可能性は十分にあります。それ自体は静的ではありません。

char *_arr = new char[20];
const char* _ptr_arr = _arr;

ここで、 my_ptr_arrが your とまったく同じ型であることに同意できますconst_char_ptrが、コンテンツが保存される場所は実行時に変更される可能性があります。テンプレートのまったく新しいインスタンス化が必要になる可能性があり、テンプレートが作成された時点から非決定論的であるため、許可されていないテンプレート内。Achar [6]は静的であり、変化しません。

foo( add_strlen<_ptr_arr> );

次のコンパイラ エラーが発生します。

test.cpp:36:5: error: no matching function for call to 'foo'
    foo( add_strlen<_ptr_arr>);
    ^~~
test.cpp:6:5: note: candidate function not viable: no overload of 'add_strlen' matching 'my_func_ptr' (aka 'int (*)(int)') for 1st argument
int foo( my_func_ptr f ) {
    ^

これはあまり役に立ちません。有効なオーバーロードが存在しない理由を理解したいのですが、関数ポインターとして渡されずに関数スタンドアロンでコードをコンパイルすると、次の結果が得られます。

add_strlen<_ptr_arr>(0);

結果は次のとおりです。

test.cpp:36:5: error: no matching function for call to 'add_strlen'
    add_strlen<_ptr_arr>(0);
    ^~~~~~~~~~~~~~~~~~~~
test.cpp:16:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'S'
int add_strlen( int i ) {
    ^

したがって、明示的に指定された引数は無効です。具体的には、整数へのポインターを渡すことはできません。

于 2013-02-22T00:17:25.837 に答える