簡體   English   中英

使用調試斷言時避免ODR違規

[英]Avoiding ODR violations when using debug asserts

我有一個只有頭的庫,在調試模式下編譯時啟用了一些額外的失敗快速運行時斷言。 標頭的簡化版本如下所示:

#include <exception>

#ifdef MYDEBUG
#   define MYASSERT(condition) do{ if (!(condition)) std::terminate(); } while(0)
#else
#   define MYASSERT(condition)
#endif

template<typename T>
class Checker
{
public:

    T operator()(T value)
    {
        MYASSERT(value);
        return value;
    }
};

如果一個翻譯單元包括標題而沒有首先定義MYDEBUG ,而另一個翻譯單元在定義MYDEBUG之后包含它,並且我將結果對象文件鏈接在一起,那是否會構成ODR違規?

如何避免這種情況但仍然允許每個TU在包含標題時獨立指定其所需的斷言設置?

如果一個翻譯單元包括標題而沒有首先定義MYDEBUG ,而另一個翻譯單元在定義MYDEBUG之后包含它,並且我將結果對象文件鏈接在一起,那是否會構成ODR違規?

是的,它違反了單一定義規則。 這違反了內聯函數的規則,即內聯函數定義必須在所有翻譯單元中具有確切的標記。

如何避免這種情況但仍然允許每個TU在包含標題時獨立指定其所需的斷言設置?

處理的一種方法是將 MYASSERT定義為文件范圍的 static函數。

 
 
 
  
  #ifdef MYDEBUG static void MYASSERT(bool condition) { if (!(condition)) { std::terminate(); } } #else static void MYASSERT(bool condition) { // Noop } #endif
 
  

看來你做不到。 謝謝,@ RustyX。

解決方案1:使用范圍:

#ifdef MYDEBUG
#   define MYASSERT(condition) do{ if (!(condition)) std::terminate(); } while(0)
#else
#   define MYASSERT(condition)
#endif

namespace {
  template<typename T>
  class Checker
  {
  public:
      T operator()(T value)
      {
          MYASSERT(value);
          return value;
      }
  };
}

這實質上將Checker更改為內部鏈接,並且可能帶來額外成本,即它可能會多次在最終可執行文件中結束。 但是,在這種特殊情況下,沒有額外的成本,因為它可能會被內聯。

解決方案2:在調試模式下參數化模板:

更新3:使用模板專業化,感謝@ Jarod42的建議

#ifdef MYDEBUG
#   define MYASSERT(condition) do{ if (!(condition)) std::terminate(); } while(0)
#   define MYDEBUG_FLAG true
#else
#   define MYASSERT(condition)
#   define MYDEBUG_FLAG false
#endif

template<typename T, bool = MYDEBUG_FLAG> class Checker;

template<typename T>
class Checker<T, MYDEBUG_FLAG>
{
public:
    T operator()(T value)
    {
        MYASSERT(value);
        return value;
    }
};

然后,調試和非調試實例將彼此獨立。

關於這一點的Checker<T, !MYDEBUG_FLAG>是,即使一個人意外地實例化Checker<T, !MYDEBUG_FLAG> ,它也不會編譯,因此不會違反ODR(只提供一個版本,無論是調試版本還是非調試版本, TU)。

第一個RustyX的答案的變體,但我認為固定:

#ifdef MYDEBUG
#   define MYDEBUG_FLAG true
#else
#   define MYDEBUG_FLAG false
#endif

#define MYASSERT(condition) do{ if (!(condition)) std::terminate(); } while(0)

// Following declaration differs, but doesn't break ODR.
template<typename T, bool = MYDEBUG_FLAG> class Checker;

// And both definitions of specialization.
template <typename T>
class Checker<T, true>
{
public:

    T operator()(T value)
    {
        MYASSERT(value);
        return value;
    }
};

template <typename T>
class Checker<T, false>
{
public:

    T operator()(T value)
    {
        return value;
    }
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM