7

特定のポリシーまたはタイプでは機能しないいくつかのメソッドを持つ複雑なテンプレート クラスがあります。したがって、これらの型を (コンパイル時に型特性を使用して) 検出すると、ナイス メッセージと共に静的アサーションを起動します。

現在、手動でテンプレートをインスタンス化する作業も数多く行っています。部分的には、メソッドが構文チェックのためにコンパイラーに強制されるようにするためです。また、ライブラリ ユーザーのコンパイル時間も短縮されます。問題は、静的アサーションが常に起動されるため、問題のテンプレート クラスを手動でインスタンス化できないことです。

これに対する回避策はありますか?

編集:より明確にするために、ここに例を示します (この場合の明示的なインスタンス化は someFunc1() で失敗します:

// header
template <typename T>
class someClass
{
  void someFunc() {}
  void someFunc1() { static_assert(false, assertion_failed); }
};

// source
template someClass<int>; // Explicit instantiation

EDIT2:これは別の例です。今回は、それをコンパイルして、私が何を意味するかを確認できます。まず、すぐにコンパイルします。コードがコンパイルされるはずです。次に[2] のコメントを外すと、静的アサーションが起動します。[2] をコメントアウトし、 [1]をコメント解除します。テンプレートを明示的にインスタンス化しているため、静的アサーションが発生します。明示的なインスタンス化にはメリットがあるため、削除は避けたいと思います (メリットについては上記を参照してください)。

namespace Loki
{
  template<int> struct CompileTimeError;
  template<> struct CompileTimeError<true> {};
}

#define LOKI_STATIC_CHECK(expr, msg) \
    { Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }

template <typename T>
class foo
{
public:

  void func() {}
  void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); }
};

template foo<int>;
//template foo<double>; // [1]

int main()
{
  foo<int> a;
  a.func1();

  foo<double> b;
  //b.func1(); //[2]

  return 0;
}
4

4 に答える 4

3

両方を持つことはできません: インスタンス化を防ぎ、型明示的にインスタンス化する静的アサーションを持つことはできません! これは明らかな矛盾です。ただし、多少面倒ですが、条件付きで機能を含めることができます。特定のメンバー関数が特定の型でサポートされていない場合は、この関数を条件付きでそれを持つ基本クラスに移動できます。 . この方法では、静的アサーションを使用せず、メンバー関数を削除するだけです。これにより、メンバー変数の場所など、他の興味深い問題が発生することは承知していますが、あなたが説明しているコンテキストでは、これが得られる最善のものだと思います。

これがどのように見えるかの簡単な例を次に示します。

template <typename T, bool = std::numeric_limits<T>::is_integer> struct foo_base;
template <typename T> struct foo_base<T, false> { /* intentionally left blank */ };
template <typename T> struct foo_base<T, true> { void foo() { /*...*/ } };

template <typename T>
struct Foo: foo_base<T> { /* .... */ };

template struct Foo<int>;    // will have foo()
template struct Foo<double>; // will not have foo()
于 2012-01-21T23:51:19.000 に答える
3

わかりましたので、明示的なインスタンス化を使用してすべてのメソッドのインスタンス化を強制している場合、enable_if. エラーをランタイムに移動するのは簡単ですが、それは望ましくありません。

あなたができる最善のことは、エラーをリンク時に移動することだと思います。これにより、禁止された関数を呼び出す可能性のあるコードパスがプログラムに含まれていないことが静的に保証されますが、エラーメッセージは、そうでない人にはあまり役に立ちません。あなたが課している制限について知りません。とにかく、解決策は、禁止されているメンバー関数の特殊化を宣言することですが、それらを定義しないことです。

template<typename T>
struct Foo {
    void bar() {
        std::cout << "bar\n";
    }
    void baz() {
        std:: cout << "baz\n";
    }
};

template<> void Foo<int>::baz(); // use of Foo<int>::baz() will resolve to this specialization, and linking will fail 

template struct Foo<int>;
template struct Foo<char>;

int main() {
    Foo<int> f;
    f.bar();
    // f.baz(); // uncommenting this line results in an ugly link time error
    Foo<char> b;
    b.bar();
    b.baz();  // works with Foo<char>
}

静的アサートは、クライアント コードでミスがあった場合に適切なエラー メッセージを表示するのに役立たなくなりましたが、特殊化を提供するのを忘れた場合に発生するため、残しておくことをお勧めします。

于 2012-01-21T22:21:04.187 に答える
1

enable_ifは、正確なテンプレート メソッドのターゲティングのための柔軟なメカニズムであり、あなたが求めているものかもしれません。例:

#include <string>
#include <iostream>

#include <boost/utility.hpp>
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>

template <class T> class mywrapper
{
  T _value;

  template <class V>
  typename boost::enable_if<boost::is_scalar<V>, void>::type printval_(V const& value)
  {
    BOOST_STATIC_ASSERT(boost::is_scalar<V>::value);
    std::cout << "scalar: " << value << std::endl;
  }

  template <class V>
  typename boost::enable_if<boost::is_compound<V>, void>::type printval_(V const& value)
  {
    BOOST_STATIC_ASSERT(boost::is_compound<V>::value);
    std::cout << "compound: " << value << std::endl;
  }

public:
  mywrapper(T const& value):_value(value) { }
  void printval() { printval_(_value); }
};

template class mywrapper<int>;
template class mywrapper<std::string>;

int main()
{
  mywrapper<int> ival(333);
  mywrapper<std::string> sval("test");

  ival.printval();
  sval.printval();
  return 0;
}
于 2012-01-21T23:31:45.767 に答える
0

bobahが提案したようにテストする機会はありませんenable_ifでしたが、ブーストを必要とせず、元の要件をある程度満たすソリューションを思いつきました(完全ではなく、良いと言い、最後に説明します)

解決策は、一部の選択されたタイプでコンパイルされた場合に失敗し、他のタイプでは問題ないダミー テンプレートをコードに配置することです。そう:

struct dummyStruct {};

#define DUMMY_TEMP typename dummy
#define DUMMY_PARAM dummyStruct

namespace Loki
{
  template<int> struct CompileTimeError;
  template<> struct CompileTimeError<true> {};
}

#define LOKI_STATIC_CHECK(expr, msg) \
{ Loki::CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }

template <typename T>
class foo
{
public:

  void func() {}
  template <typename T_Dummy>
  void func1() { LOKI_STATIC_CHECK(sizeof(T) == 4, Assertion_error); }
};

template foo<int>;
template foo<double>; // [1]

int main()
{
  foo<int> a;
  a.func1<DUMMY_PARAM>();

  foo<double> b;
  //b.func1<DUMMY_PARAM>(); //[2] - this is a static error

  return 0;
}

私のすべてのテンプレート コードで、これらの種類の関数 (つまり、静的アサートを持つもの、または型特性を使用して一部の型で動作し、他の型で失敗する可能性があるもの [この場合、異なる型に対していくつかの異なる関数が選択されます])クライアントから隠されています。したがって、私の実装では、エクストラを追加することdummy parameterは問題ありません。

おまけとして、この機能は特定のタイプのみが使用するように設計されていることがわかります。さらに、明示的なインスタンス化に関する私の最初の問題は、この単純な手法によって解決されます。

于 2012-04-09T22:53:12.950 に答える