![](/img/trans.png)
[英]Possible ODR-violations when using a constexpr variable in the definition of an inline function (in C++14)
[英]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.