【问题标题】:Using BOOST_STRONG_TYPEDEF to differentiate arg types but causing seg fault使用 BOOST_STRONG_TYPEDEF 区分 arg 类型但导致 seg 错误
【发布时间】:2021-04-14 17:34:36
【问题描述】:

我有一个方法需要多个相同枚举类型的变量。为了让编译器检测我是否传递了错误的参数,我使用了BOOST_STRONG_TYPEDEF。但是,当我创建一个实例并在 IF 语句中进行比较时,我遇到了一个段错误。

Boost 版本是 1.74

enum class Testable
{
    UNDEFINED,
    A,
    B
};

BOOST_STRONG_TYPEDEF(Testable, SomeType)

int main()
{  
    SomeType abc{Testable::UNDEFINED};
    std::cout << "START" << std::endl;
    
    if(abc == Testable::UNDEFINED)  // Seg faults here
    {
        volatile int j = 0;
    }
    
    std::cout << "FINISH" << std::endl;
}

GDB 回溯表明这是堆栈溢出/递归调用:

#1    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#2    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#3    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#4    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#5    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#6    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:

BOOST_STRONG_TYPEDEF 的文档不多。是不是我用错了?

Boost 版本是 1.74。我正在使用 Clang。

【问题讨论】:

  • 既然enum class 本身就是一个强类型,我不清楚为什么需要在它上面加上一些其他的“强类型定义”。
  • @SamVarshavchik 我有一个函数处理 4 个该类型的实例。他们有上下文差异。为了防止传递错误的实例,我使用 Boost Strong Type 来有效地创建“子类型”。
  • Hmm... 我只想声明四个包含此 enum class 实例的 structs 作为它们唯一的成员,定义一个默认的 == 和 != 运算符,以及一个构造函数吞下枚举类(可能没有必要,这要归功于 C++ 中的聚合初始化)。

标签: c++ boost


【解决方案1】:

消毒剂说

==3044==ERROR: AddressSanitizer: stack-overflow on address 0x7ffcc58b3ff8 (pc 0x56310c340e84 bp 0x7ffcc58b4000 sp 0x7ffcc58b3ff
0 T0)
    #0 0x56310c340e84 in boost::operators_impl::operator==(Testable const&, SomeType const&) /home/sehe/custom/boost_1_75_0/boo

问题在于 Boost 的 STRONG_TYPEDEF 使派生类型与基类型完全排序:

struct SomeType
    : boost::totally_ordered1<SomeType, boost::totally_ordered2<SomeType, Testable>>
{
    Testable t;
    explicit SomeType(const Testable& t_) noexcept((boost::has_nothrow_copy_constructor<Testable>::value)) : t(t_) {}
    SomeType() noexcept( (boost::has_nothrow_default_constructor<Testable>::value)) : t() {}
    SomeType(const SomeType& t_) noexcept( (boost::has_nothrow_copy_constructor<Testable>::value)) : t(t_.t) {}
    SomeType& operator=(const SomeType& rhs) noexcept((boost::has_nothrow_assign<Testable>::value)) {
        t = rhs.t;
        return *this;
    }
    SomeType& operator=(const Testable& rhs) noexcept((boost::has_nothrow_assign<Testable>::value)) {
        t = rhs;
        return *this;
    }
    operator const Testable&() const { return t; }
    operator Testable&() { return t; }
    bool operator==(const SomeType& rhs) const { return t == rhs.t; }
    bool operator<(const SomeType& rhs) const { return t < rhs.t; }
};

如果您删除隐式转换的来源:

struct SomeType
    : boost::totally_ordered1<SomeType
      /*, boost::totally_ordered2<SomeType, Testable>*/>
{
     // ...

它 JustWorks(TM)。我认为您也应该使用转换运算符explicit 并始终进行强制转换:

Live On Coliru

#include <boost/serialization/strong_typedef.hpp>
#include <iostream>

enum class Testable { UNDEFINED, A, B };
       
struct SomeType
    : boost::totally_ordered1<SomeType
      /*, boost::totally_ordered2<SomeType, Testable>*/>
{
    Testable t;
    explicit SomeType(const Testable& t_) noexcept((boost::has_nothrow_copy_constructor<Testable>::value)) : t(t_) {}
    SomeType() noexcept( (boost::has_nothrow_default_constructor<Testable>::value)) : t() {}
    SomeType(const SomeType& t_) noexcept( (boost::has_nothrow_copy_constructor<Testable>::value)) : t(t_.t) {}
    SomeType& operator=(const SomeType& rhs) noexcept((boost::has_nothrow_assign<Testable>::value)) {
        t = rhs.t;
        return *this;
    }
    SomeType& operator=(const Testable& rhs) noexcept((boost::has_nothrow_assign<Testable>::value)) {
        t = rhs;
        return *this;
    }
    explicit operator const Testable&() const { return t; }
    explicit operator Testable&() { return t; }
    bool operator==(const SomeType& rhs) const { return t == rhs.t; }
    bool operator<(const SomeType& rhs) const { return t < rhs.t; }
};

int main() {
    SomeType abc{ Testable::UNDEFINED };
    std::cout << "START" << std::endl;

    if (abc == SomeType{Testable::UNDEFINED}) {
        volatile int j = 0;
    }

    std::cout << "FINISH" << std::endl;
}

【讨论】:

  • 好消息是:您可以只使用explicit 所有构造函数和转换运算符。坏消息是它看起来确实很容易,自 c++20 以来的行为发生了变化,导致它“错误编译”(它是正确的代码生成,但与 c++17 的行为不同):i.imgur.com/flcj4Cs.png 这可以报告给Boost Operator 开发人员
  • 将此简化为一个新问题并提出stackoverflow.com/questions/65648897/…
  • 哦,删除 totally_ordered2 基类仍然可以解决问题 - 如果您不确定。原则上我觉得这个问题很有趣,但这不应该让你太担心:)
  • 相关现有库问题:github.com/boostorg/utility/issues/65,也有大量上下文
  • @user997112 我可以使用explicit 转换运算符en.cppreference.com/w/cpp/language/cast_operator
猜你喜欢
  • 2021-06-17
  • 1970-01-01
  • 2012-07-02
  • 2010-11-23
  • 1970-01-01
  • 2020-09-28
  • 1970-01-01
  • 2020-01-20
  • 2014-07-01
相关资源
最近更新 更多