7
// problem.cpp:
#include <string>

template<typename T> void func(const T & v);

int main() {
        int i;
        float f;
        char * cp;
        char ca[4];

        func(i);
        func(f);
        func(cp);
        func(std::string("std::string"));
        func(ca);
        func("string_literal");

        return 0;
}

// problem2.cpp
#include <string>

template<typename T> void func(const T & v);

// undefined reference to `void func<int>(int const&)'
template<> void func<int>(const int & v) { }

// undefined reference to `void func<float>(float const&)'
template<> void func<float>(const float & v) { }

// undefined reference to `void func<char*>(char* const&)'
template<> void func<char *>(char * const & v) { }

// void func<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
template<> void func<std::string>(std::string const & v) { }

// undefined reference to `void func<char [4]>(char const (&) [4])'
// ???

// undefined reference to `void func<char [15]>(char const (&) [15])'
// ???

2 つの解決策が見つかりました。

a) problem2.cpp:

template<> void func<char[4]>(const char (&v)[4]) { }
template<> void func<char[15]>(const char (&v)[15]) { }

b) problem.cpp:

template<typename T, unsigned N> void func(const T (&v)[N]) { func(v+0); }
and then in problem2.cpp, add the newly missing
template<> void func<const char *>(const char * const & v) { }

申し訳ありませんアカッパ、それらが2つの独立したソリューションであることを明確にするために再度編集する必要がありました...

akappa: この質問に何かを追加する唯一の方法は、編集することです。コメントすることも、回答を追加することもできません。「スタック オーバーフローには、別のドメインからの外部 JavaScript が必要であり、ブロックされているか、読み込みに失敗しています。」と関係がある可能性があります。正確に SO が私に伝えようとしていることがわからないため、解決方法がわかりません。

4

1 に答える 1

3

関数の特殊化は、引数の型と正確に一致する必要があるという点で注意が必要です。配列(文字列リテラルも配列)の場合、コンパイラは型の推定を実行し、正確な型が何であるかを調べてから、プログラム内でその特定の記号を探します。

特に、のタイプcachar[4]であるため、テンプレートを呼び出すと、推定タイプはT == char[4]であり、検出されると予想される関数シグネチャはですvoid func<>( const char (&)[4] ) 。2つのソリューションの時点では、これらは完全に異なるアプローチです。最初のケースでは、使用される特定のタイプのテンプレートを特殊化しています。使用する新しい文字列リテラルのサイズまたはタイプごとに、手動で特殊化を追加する必要があるため、これは面倒になります。ちなみに、これがテンプレートをヘッダーで定義する必要がある理由です(多くの場合)。そのため、明示的なインスタンス化ですべての可能なテンプレート引数に名前を付ける必要はありません(*) ...

2番目の解決策は完全に異なります。この場合、2番目の無関係な(ある程度)ベーステンプレートを作成しています。その基本テンプレートは、配列の最初の要素へのポインターを取得し、それを使用して元のテンプレートを呼び出し、タイプを効果的に変更します(そして、プロセスで情報を失います:サイズが失われます)。この時点で、配列を使用したすべての呼び出しはその2番目のテンプレートと一致し、ポインターを使用して元のテンプレートへの呼び出しとして転送されるため、配列のサイズに特化する必要がなくなります(コンパイラーはこれらの特殊化を解決します)。

また、char配列の受け渡しのみを許可する場合、ディスパッチャーテンプレートはすべての型を受け入れる必要はなく、型以外の引数を1つ持つことができます。

template <std::size_t N>
void f( const char (&a)[N] ) { f( &a[0] ); }

まとめ:

関数のテンプレートの特殊化は、処理が面倒なので避けてください。配列のタイプにはサイズが含まれます。つまり、潜在的な配列サイズごとに特化する必要があります。または、元のテンプレートにディスパッチするセカンダリテンプレートを追加して、ポインタへの変換を実行することもできます。


*すべての特殊化の実装が同じである場合、.cppでテンプレートの定義を提供し、テンプレートを手動でインスタンス化することで、同じ動作を維持しながら、特殊化(およびコードのコピー)を回避できることに注意してください。

template <typename T> void func( T const & ) {
   // code goes here
}
template void func<int>( int const & );
template void func<double>( double const & );
//...
于 2012-08-01T13:13:26.250 に答える