2

私のテンプレートでは、型名が基本型かどうかに基づいて、さまざまなコード部分が必要です。

このコードをコンパイルすると、MSVC で C4067 が返されます (プリプロセッサ ディレクティブに続く予期しないトークン - 改行が必要です):

template <typename T>
void MyClass<T>::foo()
{
// ... some code here
#if std::is_fundamental<T>::value
    if(m_buf[j] < m_buf[idx_min])
        idx_min = j;
#else
    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]);
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]);
    // error checking removed for simplicity
    if(curr.before(curr_min))
        idx_min = j;
}
#endif

テンプレートは、プリミティブ データ型と独自の (ASSortable から派生した) データ型の両方で動作し、テンプレートのインスタンス化コードからエラーがスローされます。

template class MyClass<char>;

プリコンパイラ式をこれに変更しようとしてもうまくいきませんでした:

#if std::is_fundamental<T>::value == true

同じ正確な警告を生成します。

このコードを警告なしにする方法はありますか?

編集頭に浮かぶもう1つのことは、これをランタイムチェックに変換し、「定数if式」警告を受け入れることです...特殊化も余分な肥大化もなしに、単一の関数でこれをエレガントに行う方法は本当にありませんか? ?

編集#2したがって、これを解決する方法(これは明らかでしたが、どういうわけか私を逃れました...)はbool ASSortable::operator<(const ASSortable& _o) const {return this->before(_o);};、仕事をしてコードをきれいにする a を定義することでした(もう一度)。

ifコード内にs や#ifdefs などの混乱はもうありません。

こんなに明白でシンプルな答えがあったので、私がその質問をしたとは信じられません:(

4

5 に答える 5

2

その問題を解決するための一般的なパターンは、関数を特殊化された基本クラスに移動し、継承を悪用してスコープに持ち込むことです。

template <typename T, bool is_fundamental>
struct Foo_impl {
   void foo() {
   }
};
template <typename T>
struct Foo_impl<T,true>
{
   void foo() {              // is fundamental version
   }
};
template <typename T>
class Foo : public Foo_impl<T, std::is_fundamental_type<T>::value> {
   // ...
};

foo別のアプローチは、それらをクラスのプライベート関数として実装し、特性に基づいて内部からディスパッチすることです。これは非常にシンプルでクリーンなソリューションですが、 の 2 つのバージョンのいずれかがfoo_implコンパイルされない場合は失敗します。その場合、他の人が解決するテンプレートメンバー関数を提案しているように使用できますが非テンプレートfooをパブリックインターフェイスとして提供し、プライベートfoo_implテンプレートに転送します。その理由は、インターフェイスの一部ではなく、条件付きコンパイルをハックtemplateするための実装の詳細があるためです。独自のクラスの型とは異なるテンプレート引数を使用してそのメンバー関数を呼び出すユーザー コードは必要ありません。pmrの回答からの借用:

template <typename T>
struct Foo
{
  template <typename U = T, 
            typename std::enable_if< 
              std::is_fundamental<U>::value, int >::type* _ = 0
           >
  void foo() {
    std::cout << "is fundamental" << std::endl;
  }
//...

そのソリューションは、次のようなユーザー コードを許可します。

Foo<int> f;
f.foo<std::string>();

必要のない関数をインスタンス化し、不要なロジックを実行します。ユーザーがあなたのクラスをだまそうとしなくても、それがインターフェイスのテンプレートであるという事実は混乱を招き、さまざまな型に対してそれを呼び出すことができるとユーザーに思わせる可能性があります。

于 2012-07-12T14:06:11.543 に答える
1

プリプロセッサは、コンパイラが型を分析して の意味を認識する前のコンパイルの初期段階で実行されるため、std::is_fundamental<T>::valueこの方法では機能しません。

代わりに、特殊化を使用します。

template<bool> void f();

template<> void f<true>() {
    if(m_buf[j] < m_buf[idx_min])
        idx_min = j;
}

template<> void f<false>() {
    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]);
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]);
    // error checking removed for simplicity
    if(curr.before(curr_min))
        idx_min = j;
}

template <typename T>
void MyClass<T>::foo()
{
// ... some code here
    f<std::is_fundamental<T>::value>();
}

編集:メンバー関数を作成する必要がある可能性がありますが、特殊化されていないテンプレートでfあるため、直接行うことはできません。の正しいメンバーに呼び出しを委任するグローバルをMyClass<T>作成できます。ただし、別のアプローチがあります。fMyClass

オーバーロードを使用すると、次のようになります。

void MyClass<T>::f(const true_type&) {
    if(m_buf[j] < m_buf[idx_min])
        idx_min = j;
}

void MyClass<T>::f(const false_type&) {
    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]);
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]);
    // error checking removed for simplicity
    if(curr.before(curr_min))
        idx_min = j;
}

template <typename T>
void MyClass<T>::foo()
{
// ... some code here
    f(std::is_fundamental<T>::type());
}
于 2012-07-12T12:45:14.803 に答える
1

コンパイルの状態を混同しています。プリプロセッサは実際のコンパイラの前に実行され、型やテンプレートの知識はありません。(非常に) 洗練されたテキスト置換を実行するだけです。

現在の C++ にはstatic if1などはないため、条件付きコンパイルを有効にするには別の方法に頼る必要があります。関数については、私が好むでしょうenable_if

#include <type_traits>
#include <iostream>

template <typename T>
struct Foo
{
  template <typename U = T, 
            typename std::enable_if< 
              std::is_fundamental<U>::value, int >::type = 0
           >
  void foo() {
    std::cout << "is fundamental" << std::endl;
  }

  template <typename U = T, 
            typename std::enable_if< 
              !(std::is_fundamental<U>::value), int >::type = 0
           >
  void foo() {
    std::cout << "is not fundamental" << std::endl;
  }
};


struct x {};

int main()
{
  Foo<int> f; f.foo();
  Foo<x> f2; f2.foo();
  return 0;
}

1参考文献:

ビデオ: ゴーイング ネイティブで Alexandrescu によって提示された場合は静的です。

n3322 : ウォルター・E・ブラウンの提案static if

n3329 : サッター、ブライト、アレクサンドレスクの提案static if

于 2012-07-12T13:04:06.663 に答える
0

std::is_fundamental<T>::value == true前処理時に使用することはできません。std :: enable_if:でSFINAEトリックを使用する必要があると思います。

template <typename T>
typename std::enable_if<std::is_fundamental<T>::value, void>::type 
MyClass<T>::foo()
{
    // ... some code here

    if(m_buf[j] < m_buf[idx_min])
        idx_min = j;
}


template <typename T>
typename std::enable_if<!std::is_fundamental<T>::value, void>::type 
MyClass<T>::foo()
{
    // ... some code here

    const ASSortable& curr = dynamic_cast<ASSortable&>(m_buf[j]);
    const ASSortable& curr_min = dynamic_cast<ASSortable&>(m_buf[idx_min]);
    // error checking removed for simplicity
    if(curr.before(curr_min))
        idx_min = j;
}
于 2012-07-12T12:48:48.623 に答える
0

::プリプロセッサ ディレクティブでは使用できません。実際、後で使用できる#ifのは、コンパイル時より前に定義された定数式だけです。ここでいくつかの情報を見つけることができます

于 2012-07-12T12:45:51.317 に答える