デフォルト関数のないC ++テンプレートの特殊化
-
06-07-2019 - |
質問
コンパイルして正常に動作する次のコードがあります:
template<typename T>
T GetGlobal(const char *name);
template<>
int GetGlobal<int>(const char *name);
template<>
double GetGlobal<double>(const char *name);
ただし、<!> quot; default <!> quot;を削除します。関数。つまり、GetGlobal <!> lt; t <!> gt;へのすべての呼び出しを行います。ここで、「t」はintまたはdoubleのエラーではありません。
たとえば、GetGlobal <!> lt; char <!> gt;()はコンパイル時エラーです。
デフォルトの関数を削除しようとしましたが、想像したとおり、多くのエラーを受け取りました。<!> quot; disable <!> quot;関数の特別なバージョンへの呼び出しのみを許可しますか?
ありがとう!
解決
コンパイル時エラーを取得するには、次のように実装します。
template<typename T>
T GetGlobal(const char *name) { T::unimplemented_function; }
// `unimplemented_function` identifier should be undefined
Boostを使用すると、よりエレガントにできます:
template<typename T>
T GetGlobal(const char *name) { BOOST_STATIC_ASSERT(sizeof(T) == 0); }
C ++標準では、sizeofが0のような型が存在しないことが保証されているため、コンパイル時エラーが発生します。
sbi のコメントで示唆されているように、最後のものは次のように減らすことができます。
template<typename T>
T GetGlobal(const char *name) { char X[!sizeof(T)]; }
私は最初の解決策を好みます。それは他の解決策よりも明確なエラーメッセージを(少なくともVisual C ++では)与えるからです。
他のヒント
これは古くて時代遅れの質問ですが、C++11
が削除された関数を使用してこの問題を解決したことに注意する価値があります。
template<typename T>
T GetGlobal(const char *name) = delete;
template<>
int GetGlobal<int>(const char *name);
更新
これは、MacOS llvm 8
ではコンパイルされません。
これは、4年前にまだ問題が残っているためです(このバグレポートを参照してください) )。
次の回避策は問題に適合します(static_assert
構造を使用)。
template<typename T>
T GetGlobal(const char *name) {
static_assert(sizeof(T) == 0, "Only specializations of GetGlobal can be used");
}
template<>
int GetGlobal<int>(const char *name);
更新
Visual Studio 15.9にも同じバグがあります。以前の回避策を使用してください。
実装しない場合、少なくともリンカーエラーが発生します。コンパイル時エラーが必要な場合は、クラステンプレートを使用してこれを行うことができます。
template<typename T>
struct GlobalGetter;
template<>
struct GlobalGetter<int> {
static int GetGlobal(const char *name);
};
template<>
struct GlobalGetter<double> {
static double GetGlobal(const char *name);
};
template<typename T>
T GetGlobal(const char *name)
{
return GlobalGetter<T>::GetGlobal(name);
}
実際に実装を提供するのではなく、メソッドの単なる宣言を提供することをお勧めします。
他のオプションは、コンパイル時のアサートを使用することです。 Boostにはそのような獣がたくさんいます。
namespace mpl = boost::mpl;
BOOST_MPL_ASSERT((mpl::or_< boost::same_type<T, double>,
boost::same_type<T, int> >));
メッセージバージョンもあります。これは役立ちます。
以下は、ブーストを使用するための代替手法です。
依存する名前にtypedefを宣言します
これは、DONTの名前検索が「T」が置換されたときにのみ行われるため、機能します。これは、 Kirill
template <typename T>
T GetGlobal (const char * name) {
typedef typename T::DONT CALL_THIS_FUNCTION;
}
不完全な返品タイプを使用
この手法は専門分野では機能しませんが、オーバーロードでは機能します。不完全な型を返す関数を宣言することは正当ですが、呼び出すことはできません:
template <typename T>
class DONT_CALL_THIS_FUNCTION GetGlobal (const char * name);