Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用调试断言时避免ODR冲突_C++_One Definition Rule - Fatal编程技术网

C++ 使用调试断言时避免ODR冲突

C++ 使用调试断言时避免ODR冲突,c++,one-definition-rule,C++,One Definition Rule,我有一个只有头的库,当在调试模式下编译时,它启用了一些额外的fail fast运行时断言。标题的简化版本如下所示: #include <exception> #ifdef MYDEBUG # define MYASSERT(condition) do{ if (!(condition)) std::terminate(); } while(0) #else # define MYASSERT(condition) #endif template<typename T

我有一个只有头的库,当在调试模式下编译时,它启用了一些额外的fail fast运行时断言。标题的简化版本如下所示:

#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;
    }
};
#包括
#ifdef MYDEBUG
#定义MYASSERT(条件)do{if(!(条件))std::terminate();}while(0)
#否则
#定义MYASSERT(条件)
#恩迪夫
模板
类检查器
{
公众:
T运算符()(T值)
{
MYASSERT(值);
返回值;
}
};
如果一个翻译单元包含标题,但没有首先定义
MYDEBUG
,而另一个翻译单元在定义
MYDEBUG
之后包含标题,并且我将生成的对象文件链接在一起,这会构成ODR冲突吗

如何避免这种情况,但仍然允许每个TU在包含标头时独立指定其所需的断言设置

如果一个翻译单元包含标题,但没有首先定义
MYDEBUG
,而另一个翻译单元在定义
MYDEBUG
之后包含标题,并且我将生成的对象文件链接在一起,这会构成ODR冲突吗

是的,这违反了一个定义规则。这违反了内联函数的规则,即内联函数定义必须在所有转换单元中具有精确的标记

如何避免这种情况,但仍然允许每个TU在包含标头时独立指定其所需的断言设置

一种处理方法是将
MYASSERT
定义为文件范围的
静态
函数

#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;
      }
  };
}
然后,调试和非调试实例化将彼此独立


这样做的好处是,即使一个人意外地实例化了
检查器,它也不会编译,因此也不会违反ODR(只要每个TU中只定义了一个版本,无论是调试版本还是非调试版本)。

first 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;
    }
};
#ifdef MYDEBUG
#定义MYDEBUG_标志true
#否则
#定义MYDEBUG_标志false
#恩迪夫
#定义MYASSERT(条件)do{if(!(条件))std::terminate();}while(0)
//以下声明有所不同,但不会破坏ODR。
模板类检查器;
//以及专业化的两个定义。
模板
类检查器
{
公众:
T运算符()(T值)
{
MYASSERT(值);
返回值;
}
};
模板
类检查器
{
公众:
T运算符()(T值)
{
返回值;
}
};

为什么不使用
MYDEBUG
作为转换运算符的编译时参数
T操作符()(T值){return do_conversion(value,MYDEBUG);}
,其中
do_conversion
可能包含也可能不包含assertNope,仍然违反了。单一定义的思想是避免链接器必须折叠多个定义时出现问题,例如,如果函数是虚拟的或通过指针调用。实际上只剩下一个版本。
一个带有外部链接的[…]内联函数,[…]非静态函数模板,[…]可以有多个定义,只要这些定义满足以下要求。
@sp2danny-当然。仍然违反ODR的是
operator()
。我相信这段代码在技术上仍然违反ODR,因为模板参数的默认值不是定义的一部分。部分专门化应该可以解决这个问题,不是吗?@Jarod42,应该可以,但我不确定您打算如何选择它?@SergeyA:Like?(声明不同,但定义相同)。@Jarod42这很聪明,我看不出为什么它不起作用。想补充一下吗?即使OP不喜欢它,我也一定会投票。@RustyX:事实上,用户在混合
MYDEBUG
标志(我认为这是一个阻塞(无法解决)问题)时,必须处理
Checker
Checker
的混合。。。
#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;
    }
};