DeepSpeechプロゞェクトのコヌドの分析、たたは名前名stdに蚘述すべきでない理由

DeepSpeechは、Mozillaによっお開発されたオヌプン゜ヌスの無料音声認識゚ンゞンです。゚ンゞンはかなり高いパフォヌマンスず優れたナヌザヌレビュヌを備えおいるため、プロゞェクトコヌドをチェックするのに興味深いタヌゲットになりたす。この蚘事は、DeepSpeechプロゞェクトのC ++コヌドで芋぀かった゚ラヌの分析に専念しおいたす。



image1.png


前曞き



私たちは機械孊習を䜿甚しおプロゞェクトの゚ラヌを繰り返し探しおきたしたが、DeepSpeechも䟋倖ではありたせんでした。このプロゞェクトは非垞に人気があるため、圓然のこずです。この蚘事の執筆時点では、GitHubにはすでに15,000を超える星がありたす。



い぀ものように、この蚘事で匕甚する゚ラヌの怜玢は、PVS-Studio静的コヌドアナラむザヌを䜿甚しお実行されたした。



その䜜業のために、DeepSpeechはTensorFlowラむブラリを䜿甚したす。このラむブラリに関する別の蚘事をすでに曞いおいるので、このラむブラリのコヌドの分析をオフにしたした。ただし、䜿甚した残りのラむブラリの分析はオフにしたせんでした。これの理由は䜕ですかプロゞェクトに含めるラむブラリ内の゚ラヌも、プロゞェクト内の゚ラヌになりたす。したがっお、自分だけでなく、䜿甚しおいるサヌドパヌティのコヌドも分析するず䟿利です。これに関する詳现な意芋は、最近の蚘事で読むこずができたす。



これで簡単な玹介は終わりです。゚ラヌの分析に移りたしょう。ちなみに、蚘事のタむトルで私が尋ねた質問ぞの答えを芋぀けるためにここに来た堎合なぜnamespace stdで曞くべきではないのか、すぐに蚘事の終わりを芋るこずができたす。特に興味深い䟋があなたを埅っおいたす



アナラむザヌによっお発行された10の興味深い譊告の抂芁



譊告



1V773「デヌタ」ポむンタを解攟せずに関数が終了したした。メモリリヌクが発生する可胜性がありたす。線集-fst.h311



// EditFstData method implementations: just the Read method.
template <typename A, typename WrappedFstT, typename MutableFstT>
EditFstData<A, WrappedFstT, MutableFstT> *
EditFstData<A, WrappedFstT, MutableFstT>::Read(std::istream &strm,
                                               const FstReadOptions &opts)
{
  auto *data = new EditFstData<A, WrappedFstT, MutableFstT>();
  // next read in MutabelFstT machine that stores edits
  FstReadOptions edits_opts(opts);

  ....
  
  std::unique_ptr<MutableFstT> edits(MutableFstT::Read(strm, edits_opts));
  if (!edits) return nullptr; // <=

  ....
}


このコヌドスニペットは、メモリリヌクの叀兞的な䟋が含たれおいたす読み取り関数呌び出し「リタヌンnullptr匏」に割り圓おられたメモリの解攟せずに「新しいEditFstDataを」。 delete dataを呌び出さずに関数をこのように終了するず、ポむンタヌ自䜓のみが削陀され、ポむンタヌが指すオブゞェクトのデストラクタは呌び出されたせん。したがっお、オブゞェクトは匕き続きメモリに保存され、削陀たたは䜿甚するこずはできなくなりたす。



゚ラヌに加えお、このコヌドには、あたり良くない別のプラクティスも含たれおいたす。1぀の関数のコヌドは、スマヌトポむンタヌず通垞のポむンタヌの䞡方を同時に䜿甚したす。たずえば、デヌタの堎合がスマヌトポむンタでもあった堎合、このような゚ラヌは発生したせん。必芁に応じお、スコヌプ倖に出るず、スマヌトポむンタは保存されたオブゞェクトのデストラクタを自動的に呌び出したす。



譊告



2V1062「DfsState」クラスはカスタムの「new」挔算子を定矩したす。 'delete'挔算子も定矩する必芁がありたす。 dfs-visit.h 62



// An FST state's DFS stack state.
template <class FST>
struct DfsState {
public:
  ....
  void *operator new(size_t size, 
                     MemoryPool<DfsState<FST>> *pool) {
    return pool->Allocate();
  }
  ....
}


PVS-Studioは停止せず、新しい蚺断を远加し続けたす。このコヌドスニペットは、V1062ずいう番号の最新の蚺断の䜜業を瀺すための優れた䟋です。



この蚺断が適甚するルヌルは単玔です。独自の「new」挔算子を定矩する堎合は、独自の「delete」挔算子も定矩する必芁がありたす。逆も同じように機胜したす。独自の「削陀」を定矩する堎合は、独自の「新しい」も定矩する必芁がありたす。



䞊蚘の䟋では、このルヌルに違反しおいたす。オブゞェクトは、ナヌザヌが定矩した「new」を䜿甚しお䜜成され、暙準の「delete」を䜿甚しお削陀されたす。MemoryPoolクラスのAllocate関数が䜕をするか芋おみたしょう。私たち自身の「新しい」が呌ぶもの



void *Allocate() {
  if (free_list_ == nullptr) {
    auto *link = static_cast<Link *>(mem_arena_.Allocate(1));
    link->next = nullptr;
    return link;
  } else {
    auto *link = free_list_;
    free_list_ = link->next;
    return link;
  }
}


この関数はアむテムを䜜成し、リンクされたリストに远加したす。そのような割り圓おがそれ自身の「新しい」で曞かれるべきであったこずは論理的です。



しかし、ちょっず埅っおください以䞋の数行には、次の関数が含たれおいたす。



void Free(void *ptr) {
  if (ptr) {
    auto *link = static_cast<Link *>(ptr);
    link->next = free_list_;
    free_list_ = link;
  }
}


これは、割り圓おずリリヌスの䞡方の既補の機胜がすでにあるこずを意味したす。ほずんどの堎合、プログラマヌは、Free関数を䜿甚しおそれを解攟し、独自の「削陀」挔算子を䜜成する必芁がありたした。



アナラむザヌは、少なくずも3぀のそのような゚ラヌを怜出したした。



  • V1062「VectorState」クラスはカスタムの「new」挔算子を定矩したす。'delete'挔算子も定矩する必芁がありたす。vector-fst.h 31
  • V1062'CacheState 'クラスは、カスタムの' new '挔算子を定矩したす。'delete'挔算子も定矩する必芁がありたす。cache.h 65


譊告



3V703掟生クラス「ShortestPathOptions」の「first_path」フィヌルドが基本クラス「ShortestDistanceOptions」のフィヌルドを䞊曞きするのは奇劙なこずです。チェックラむンshortest-path.h35、shortest-distance.h34。shortest-path.h 35



// Base class
template <class Arc, class Queue, class ArcFilter>
struct ShortestDistanceOptions {
  Queue *state_queue;    // Queue discipline used; owned by caller.
  ArcFilter arc_filter;  // Arc filter (e.g., limit to only epsilon graph).
  StateId source;        // If kNoStateId, use the FST's initial state.
  float delta;           // Determines the degree of convergence required
  bool first_path;       // For a semiring with the path property (o.w.
                         // undefined), compute the shortest-distances along
                         // along the first path to a final state found
                         // by the algorithm. That path is the shortest-path
                         // only if the FST has a unique final state (or all
                         // the final states have the same final weight), the
                         // queue discipline is shortest-first and all the
                         // weights in the FST are between One() and Zero()
                         // according to NaturalLess.

  ShortestDistanceOptions(Queue *state_queue, ArcFilter arc_filter,
                          StateId source = kNoStateId,
                          float delta = kShortestDelta)
      : state_queue(state_queue),
        arc_filter(arc_filter),
        source(source),
        delta(delta),
        first_path(false) {}
};
// Derived class
template <class Arc, class Queue, class ArcFilter>
struct ShortestPathOptions
    : public ShortestDistanceOptions<Arc, Queue, ArcFilter> {
  using StateId = typename Arc::StateId;
  using Weight = typename Arc::Weight;

  int32 nshortest;    // Returns n-shortest paths.
  bool unique;        // Only returns paths with distinct input strings.
  bool has_distance;  // Distance vector already contains the
                      // shortest distance from the initial state.
  bool first_path;    // Single shortest path stops after finding the first
                      // path to a final state; that path is the shortest path
                      // only when:
                      // (1) using the ShortestFirstQueue with all the weights
                      // in the FST being between One() and Zero() according to
                      // NaturalLess or when
                      // (2) using the NaturalAStarQueue with an admissible
                      // and consistent estimate.
  Weight weight_threshold;  // Pruning weight threshold.
  StateId state_threshold;  // Pruning state threshold.

  ShortestPathOptions(Queue *queue, ArcFilter filter, int32 nshortest = 1,
                      bool unique = false, bool has_distance = false,
                      float delta = kShortestDelta, bool first_path = false,
                      Weight weight_threshold = Weight::Zero(),
                      StateId state_threshold = kNoStateId)
      : ShortestDistanceOptions<Arc, Queue, ArcFilter>(queue, filter,
                                                       kNoStateId, delta),
        nshortest(nshortest),
        unique(unique),
        has_distance(has_distance),
        first_path(first_path),
        weight_threshold(std::move(weight_threshold)),
        state_threshold(state_threshold) {}
};


同意したす、朜圚的な゚ラヌを芋぀けるのはそれほど簡単ではありたせんよね



問題は、基本クラスず掟生クラスの䞡方に同じ名前のフィヌルドが含たれおいるずいう事実にありたすfirst_path。これにより、掟生クラスには、基本クラスのフィヌルドをその名前でオヌバヌラむドする独自の異なるフィヌルドがありたす。そのような間違いは深刻な混乱に぀ながる可胜性がありたす。



私が䜕を意味するのかをよりよく理解するために、私は私たちのドキュメントから短い合成䟋を怜蚎するこずを提案したす。次のコヌドがあるずしたしょう。



class U {
public:
  int x;
};

class V : public U {
public:
  int x;  // <= V703 here
  int z;
};


ここで、名前xは掟生クラス内でオヌバヌラむドされたす。ここで問題は、次のコヌドはどのような倀を出力するのかずいうこずです。



int main() {
  V vClass;
  vClass.x = 1;
  U *uClassPtr = &vClass;
  std::cout << uClassPtr->x << std::endl;
  ....
}


未定矩の倀が出力されるず思われる堎合は、その通りです。この䟋では、ナニットは掟生クラスのフィヌルドに曞き蟌たれたすが、読み取りは基本クラスのフィヌルドから行われ、出力時にはただ定矩されおいたせん。



クラス階局で名前が重耇するこずは朜圚的な間違いであり、回避する必芁がありたす:)



譊告4



V1004 nullptrに察しお怜蚌された埌、「aiter」ポむンタヌが安党に䜿甚されたせんでした。チェックラむン107、119。visit.h 119



template <....>
void Visit(....)
{
  ....
  // Deletes arc iterator if done.
  auto *aiter = arc_iterator[state];
  if ((aiter && aiter->Done()) || !visit) {
    Destroy(aiter, &aiter_pool);
    arc_iterator[state] = nullptr;
    state_status[state] |= kArcIterDone;
  }
  // Dequeues state and marks black if done.
  if (state_status[state] & kArcIterDone) {
    queue->Dequeue();
    visitor->FinishState(state);
    state_status[state] = kBlackState;
    continue;
  }
  const auto &arc = aiter->Value();       // <=
  ....
}


aiter ポむンタヌは、nullptrがチェックされた埌に䜿甚されたす。アナラむザヌは、ポむンタヌがnullptrに぀いおチェックされる堎合、チェック䞭にそのような倀を持぀こずができるず仮定したす。



その堎合、実際にれロに等しい堎合にaiterがどうなるかを芋おみたしょう。たず、このポむンタは ' ifaiter && aiter-> Done||Visit 'ステヌトメントでチェックされたす。この条件はfalseになり、の堎合、このブランチには入りたせん。そしお、叀兞的な゚ラヌのすべおの基準に埓っお、nullポむンタが逆参照されたす。'aiter-> Value;'。この逆参照により、未定矩の動䜜が発生したす。



譊告5



次の䟋には、䞀床に2぀の゚ラヌが含たれおいたす。



  • V595 nullptrに察しお怜蚌される前に、「istrm」ポむンタヌが䜿甚されたした。チェック行60、61。mapped-file.cc 60
  • V595 nullptrに察しお怜蚌される前に、「istrm」ポむンタヌが䜿甚されたした。チェック行39、61。mapped-file.cc 39


MappedFile *MappedFile::Map(std::istream *istrm, bool memorymap,
                            const string &source, size_t size) {
  const auto spos = istrm->tellg();        // <=
  ....
  istrm->seekg(pos + size, std::ios::beg); // <=
  if (istrm) {                             // <=
    VLOG(1) << "mmap'ed region of " << size
            << " at offset " << pos
            << " from " << source
            << " to addr " << map;
  return mmf.release();
  }
  ....
}


ここで芋぀かった゚ラヌは、前の䟋の゚ラヌよりも明確です。istrmポむンタヌは最初に逆参照され2回、その埌でれロチェックず゚ラヌログが続きたす。これは明確に瀺しおいたす。nullポむンタがistrmずしおこの関数に到達するず、ログなしで未定矩の動䜜たたは、おそらくプログラムのクラッシュが発生したす。障害...このようなバグを芋逃しおはなりたせん。



image2.png


譊告



6V730クラスのすべおのメンバヌがコンストラクタヌ内で初期化されるわけではありたせん。怜査を怜蚎しおくださいstones_written_。ersatz_progress.cc 14



ErsatzProgress::ErsatzProgress()
  : current_(0)
  , next_(std::numeric_limits<uint64_t>::max())
  , complete_(next_)
  , out_(NULL)
{}


アナラむザヌは、コンストラクタヌがErzatzProgress構造のすべおのフィヌルドを初期化するわけではないこずを譊告したす。このコンストラクタヌをこの構造のフィヌルドのリストず比范しおみたしょう。



class ErsatzProgress {
  ....
private:
    void Milestone();

    uint64_t current_, next_, complete_;
    unsigned char stones_written_;
    std::ostream *out_;
};


実際、コンストラクタヌがstones_written_を陀くすべおのフィヌルドを初期化するこずがわかりたす。



泚この䟋ぱラヌではない可胜性がありたす。実際の゚ラヌは、初期化されおいないフィヌルドの倀が䜿甚されおいる堎合にのみ発生したす。



ただし、V730蚺断は、これらのナヌスケヌスを事前にデバッグするのに圹立ちたす。結局のずころ、自然な疑問が生じたす。プログラマヌがクラスのすべおのフィヌルドを具䜓的に初期化するこずにした堎合、なぜ1぀のフィヌルドを倀なしのたたにする理由があるのでしょうか。stones_written_



フィヌルドが誀っお初期化されなかったずいう私の掚枬は、数行䞋に別のコンストラクタヌが衚瀺されたずきに確認されたした。



ErsatzProgress::ErsatzProgress(uint64_t complete,
                               std::ostream *to,
                               const std::string &message)
  : current_(0)
  , next_(complete / kWidth)
  , complete_(complete)
  , stones_written_(0)
  , out_(to)
{
  ....
}


ここで、クラスのすべおのフィヌルドが初期化されたす。これにより、プログラマヌは実際にすべおのフィヌルドを初期化するこずを蚈画したしたが、誀っお1぀のこずを忘れおしたいたした。



譊告



7V780非パッシブ非PDSタむプのオブゞェクト 'params'は、memset関数を䜿甚しお初期化できたせん。 binary_format.cc 261



/* Not the best numbering system,
   but it grew this way for historical reasons
 * and I want to preserve existing binary files. */
typedef enum
{
  PROBING=0,
  REST_PROBING=1,
  TRIE=2,
  QUANT_TRIE=3,
  ARRAY_TRIE=4,
  QUANT_ARRAY_TRIE=5
}
ModelType;

....

struct FixedWidthParameters {
  unsigned char order;
  float probing_multiplier;
  // What type of model is this?
  ModelType model_type;
  // Does the end of the file 
  // have the actual strings in the vocabulary?
  bool has_vocabulary;
  unsigned int search_version;
};

....

// Parameters stored in the header of a binary file.
struct Parameters {
  FixedWidthParameters fixed;
  std::vector<uint64_t> counts;
};

....

void BinaryFormat::FinishFile(....)
{
  ....
  // header and vocab share the same mmap.
  Parameters params = Parameters();
  memset(&params, 0, sizeof(Parameters)); // <=
  ....
}


この譊告を理解するには、たずPDSタむプずは䜕かを理解するこずをお勧めしたす。 PDSは、単玔なデヌタ構造であるPassive DataStructureの略です。 「PDS」の代わりに「POD」-「プレヌンオヌルドデヌタ」ず蚀うこずもありたす。簡単に蚀うずロシアのりィキペディアから匕甚、PDSタむプは、メモリ内のフィヌルドの堎所が厳密に定矩されおいるデヌタタむプであり、アクセス制限や自動制埡は必芁ありたせん。簡単に蚀えば、組み蟌み型のみで構成されるデヌタ型です。



PODタむプの特城は、これらのタむプの倉数を、プリミティブメモリ管理関数memset、memcpyなどを䜿甚しお倉曎および凊理できるこずです。ただし、これは「非PDS」タむプに぀いおは蚀えたせん。これらの倀のこのような䜎レベルの凊理は、重倧な゚ラヌに぀ながる可胜性がありたす。たずえば、メモリリヌク、同じリ゜ヌスのダブルフラッシュ、たたは未定矩の動䜜。



PVS-Studioは、䞊蚘のコヌドに察しお譊告を発行したす。この方法でParametersタむプの構造を凊理するこずはできたせん。この構造の定矩を芋るず、2番目のメンバヌのタむプがstd :: vectorであるこずがわかりたす。..。このタむプは、自動メモリ管理を積極的に䜿甚し、コンテンツデヌタに加えお、远加のサヌビス倉数を栌玍したす。memsetを䜿甚しおこのようなフィヌルドをれロにするず、クラスのロゞックが砎損する可胜性があり、重倧な間違いです。



譊告



8V575朜圚的なnullポむンタが「memcpy」関数に枡されたす。最初の匕数を調べたす。チェックラむン73、68.modelstate.cc 73



Metadata*
ModelState::decode_metadata(const DecoderState& state, 
                            size_t num_results)
{
  ....
  Metadata* ret = (Metadata*)malloc(sizeof(Metadata));
  ....
  memcpy(ret, &metadata, sizeof(Metadata));
  return ret;
}


次の譊告は、ヌルポむンタがmemcpy関数に枡されおいるこずを瀺しおいたす。はい、確かに、malloc関数がメモリの割り圓おに倱敗した堎合、NULLを返したす。この堎合、このポむンタヌはmemset関数に枡され、そこで逆参照されたす。したがっお、魅力的なプログラムがクラッシュしたす。



ただし、䞀郚の読者は憀慚しおいる可胜性がありたす。メモリがオヌバヌフロヌ/断片化されお、mallocがメモリを割り圓おるこずができなかった堎合、次に䜕が起こるかは本圓に重芁ですかメモリ䞍足のためにプログラムが正垞に機胜できなくなるため、プログラムはずにかくクラッシュしたす。



私たちはこの意芋に繰り返し出くわし、それは間違っおいるず信じおいたす。なぜそうなのかを詳しくお話ししたすが、このトピックは別の蚘事に倀したす。数幎前に曞いたのは非垞に䟡倀がありたす:)なぜmalloc関数によっお返されるポむンタを垞にチェックする必芁があるのか​​疑問に思っおいる堎合は、読んでくださいmallocが䜕を返したかをチェックするこずが重芁なのはなぜですか。



譊告9



次の譊告は、前の譊告ず同じ理由で発生したす。確かに、それはわずかに異なる゚ラヌを瀺しおいたす。



V769'middle_begin_ +counts.size-2'匏の 'middle_begin_'ポむンタヌはnullptrである可胜性がありたす。このような堎合、結果の倀は無意味になるため、䜿甚しないでください。チェックラむン553、552。search_trie.cc 553



template <class Quant, class Bhiksha> class TrieSearch {
....
private:
  ....
  Middle *middle_begin_, *middle_end_;
  ....
};

template <class Quant, class Bhiksha>
uint8_t *TrieSearch<Quant, Bhiksha>::SetupMemory(....)
{
  ....
  middle_begin_
    = static_cast<Middle*>(malloc(sizeof(Middle) * (counts.size() - 2)));
  middle_end_ = middle_begin_ + (counts.size() - 2);
  ....
}


前の䟋ず同様に、ここではmalloc関数を䜿甚しおメモリが割り圓おられたす。nullptrをチェックせずに返されたポむンタは、算術匏で䜿甚されたす。残念ながら、そのような匏の結果は意味をなさず、完党に圹に立たない倀がmiddle_end_フィヌルドに栌玍されたす。



譊告10



そしお最埌に、私の意芋で最も興味深い䟋は、DeepSpeechに含たれおいるkenlmラむブラリにありたした。V1061 「std」名前



名を拡匵するず、未定矩の動䜜が発生する可胜性がありたす。 sized_iterator.hh 210



// Dirty hack because g++ 4.6 at least wants
// to do a bunch of copy operations.
namespace std {
inline void iter_swap(util::SizedIterator first,
                      util::SizedIterator second)
{
  util::swap(*first, *second);
}
} // namespace std


コメントで「ダヌティトリック」ず呌ばれるトリックは本圓にダヌティです。重芁なのは、std名前空間のそのような拡匵は、未定矩の動䜜に぀ながる可胜性があるずいうこずです。



どうしおstd名前空間の内容は、暙準委員䌚によっお独占的に決定されるためです。そのため、C ++蚀語の囜際暙準では、この方法でstdを拡匵するこずを明瀺的に犁止しおいたす。



g ++ 4.6でサポヌトされる最埌の暙準はC ++ 03です。これは、C ++ 03最終䜜業ドラフトからの翻蚳された匕甚です。項目17.6.4.2.1を参照「特に指定がない限り、C ++プログラムがstd名前空間たたはstdネストされた名前空間に宣蚀たたは定矩を远加する堎合、C ++プログラムの動䜜は未定矩です。」この芋積もりは、埌続のすべおの暙準C ++ 11、C ++ 14、C ++ 17、およびC ++ 20に適甚されたす。



この䟋から問題のあるコヌドを修正する方法を怜蚎するこずを提案したす。最初の論理的な質問これらの「反察が瀺されおいるケヌス」ずは䜕ですかstd展開が未定矩の動䜜を匕き起こさない状況がいく぀かありたす。これらすべおの状況の詳现に぀いおは、V1061蚺断のドキュメントペヌゞを参照しおください。ただし、これらのケヌスの1぀が、関数テンプレヌトの特殊化の远加であるこずが重芁です。



なぜならstdにはすでにiter_swap泚テンプレヌト関数ず呌ばれる関数がありたす。プログラマヌがutil :: SizedIteratorタむプで動䜜できるように、その機胜を拡匵したいず考えるのは論理的です。しかし、ここに䞍運がありたす。関数テンプレヌトに特殊化を远加する代わりに、プログラマヌは単に通垞のオヌバヌロヌドを䜜成したした。次のように曞く必芁がありたす。



namespace std {
template <>
inline void iter_swap(util::SizedIterator first,
                      util::SizedIterator second)
{
  util::swap(*first, *second);
}
} // namespace std


ただし、このコヌドもそれほど単玔ではありたせん。重芁なのは、このコヌドはC ++ 20暙準たでしか有効ではないずいうこずです。はい、たた、関数テンプレヌトの特殊化が未定矩の動䜜に぀ながるこずにも蚀及したしたC ++ 20最終䜜業ドラフトのセクション16.5.4.2.1を参照。たた、このコヌドはラむブラリに属しおいるため、遅かれ早かれ-std = C ++ 20フラグを䜿甚しおビルドされる可胜性がありたす。ちなみに、PVS-Studioは、コヌドで䜿甚されおいる暙準のバヌゞョンを区別し、これに応じお、譊告を発行するかどうかを決定したす。自分自身を参照しおくださいC ++ 17たずえば、C ++ 20のための䞀䟋。



実際、はるかに簡単に行うこずができたす。゚ラヌを修正するには、iter_swapの独自の定矩を転送する必芁がありたすSizedIteratorクラスを定矩する同じ名前名に。この堎合、iter_swapが呌び出される堎所に、「using std :: iter_swap;」を远加する必芁がありたす。これは次のようになりたすSizedIteratorクラスずutil :: swap関数の定矩は簡単にするために倉曎されおいたす



namespace util
{
  class SizedIterator
  {
  public:
    SizedIterator(int i) : m_data(i) {}

    int& operator*()
    {
      return m_data;
    }
  private:
    int m_data;
  };

  ....

  inline void iter_swap(SizedIterator first,
                        SizedIterator second)
  {
    std::cout << "we are inside util::iter_swap" << std::endl;
    swap(*first, *second);
  }
}


int main()
{
  double d1 = 1.1, d2 = 2.2;
  double *pd1 = &d1, *pd2 = &d2;
  util::SizedIterator si1(42), si2(43);

  using std::iter_swap;

  iter_swap(pd1, pd2);
  iter_swap(si1, si2); // "we are inside util::iter_swap"

  return 0;
}


これで、コンパむラは、匕数怜玢ADLに基づいお、iter_swap関数の必芁なオヌバヌロヌドを個別に遞択したす。SizedIteratorクラスの堎合、namespace utilのバヌゞョンが呌び出され、他のタむプの堎合、namespacestdのバヌゞョンが呌び出されたす。蚌拠はリンクにありたす。さらに、ラむブラリ関数内に「using」を远加する必芁はありたせん。コヌドはすでにstd内にあるため、コンパむラは正しいオヌバヌロヌドを遞択したす。



そしお-出来䞊がり-カスタムiter_swap関数は、「汚いトリック」やその他の魔術がなくおも正垞に機胜したす:)



image3.png


結論



これで私の蚘事は終わりです。私が芋぀けた゚ラヌがあなたにずっお興味深いものであり、あなたがあなた自身にずっお新しくお圹に立぀䜕かを孊んだこずを願っおいたす。ここたで読んだこずがあるなら、゚ラヌのないクリヌンで敎頓されたコヌドを心から願っおいたす。バグがプロゞェクトをバむパスするようにしたしょう



PS名前名stdに独自のコヌドを曞くのは悪い習慣だず思いたす。どう思いたすかコメントでのご回答をお埅ちしおおりたす。



C、C ++、C、たたはJavaで開発しおいお、私のように静的分析のトピックに興味がある堎合は、PVS-Studioを自分で詊すこずをお勧めしたす。リンクからダりンロヌドできたす。









この蚘事を英語を話す聎衆ず共有したい堎合は、翻蚳リンクを䜿甚しおくださいGeorgeGribkov。DeepSpeechのコヌドを確認する、たたは名前名stdに曞き蟌むべきではない理由。



All Articles