【问题标题】:type_traits segmentation fault with std::stringstd::string 的 type_traits 分段错误
【发布时间】:2023-03-24 13:40:01
【问题描述】:

Using SFINAE to check for global operator<<?templates, decltype and non-classtypes收集信息,得到如下代码:

http://ideone.com/sEQc87

基本上,我将两个问题的代码结合起来,如果有 ostream 声明,则调用 print 函数,否则调用 to_string 方法。

取自问题 1

namespace has_insertion_operator_impl {
  typedef char no;
  typedef char yes[2];

  struct any_t {
    template<typename T> any_t( T const& );
  };

  no operator<<( std::ostream const&, any_t const& );

  yes& test( std::ostream& );
  no test( no );

  template<typename T>
  struct has_insertion_operator {
    static std::ostream &s;
    static T const &t;
    static bool const value = sizeof( test(s << t) ) == sizeof( yes );
  };
}

template<typename T>
struct has_insertion_operator :
  has_insertion_operator_impl::has_insertion_operator<T> {
};

取自问题 2

template <typename T>
typename std::enable_if<has_insertion_operator<T>::value, T>::type
print(T obj) {
    std::cout << "from print()" << std::endl;
}

template <typename T>
typename std::enable_if<!has_insertion_operator<T>::value, T>::type
print(T obj) {
    std::cout << obj.to_string() << std::endl;
}

那我的课是这样的:

struct Foo
{
public:
    friend std::ostream& operator<<(std::ostream & os, Foo const& foo);
};

struct Bar
{
public:
    std::string to_string() const
    {
        return "from to_string()";
    }
};

并测试输出:

int main()
{
    print<Foo>(Foo());
    print<Bar>(Bar());

    //print<Bar>(Foo()); doesn't compile
    //print<Foo>(Bar()); doesn't compile

    print(Foo());
    print(Bar());

    print(42);
    print('a');
    //print(std::string("Hi")); seg-fault
    //print("Hey");
    //print({1, 2, 3}); doesn't compile
    return 0;
}

print(std::string("Hi")); 线段错误。谁能告诉我为什么?

【问题讨论】:

  • 您的代表对您做了什么,以至于您如此急切地分发它? :)
  • @jrok:我感觉到“如何对待好但低代表用户”类型的实验......?

标签: c++ templates segmentation-fault sfinae


【解决方案1】:

您的两个函数 print() 都应该返回一些东西,而不是返回任何东西(与您链接的问答中的版本不同)。这是 C++11 标准第 6.6.3/2 段中未定义的行为。

如果print()不应该返回任何东西,让它返回void,并将SFINAE约束放在模板参数列表中:

template <typename T,
    typename std::enable_if<
        has_insertion_operator<T>::value, T>::type* = nullptr>
void print(T obj) {
    std::cout << "from print()" << std::endl;
}

template <typename T,
    typename std::enable_if<
        !has_insertion_operator<T>::value, T>::type* = nullptr>
void print(T obj) {
    std::cout << obj.to_string() << std::endl;
}

这是包含上述更改的live example

如果您使用 C++03 并且无法为函数模板参数指定默认参数,请避免将类型指定为 std::enable_if 的第二个模板参数,或指定 void

template <typename T>
typename std::enable_if<has_insertion_operator<T>::value>::type
print(T obj) {
    std::cout << "from print()" << std::endl;
}

template <typename T>
typename std::enable_if<!has_insertion_operator<T>::value>::type
print(T obj) {
    std::cout << obj.to_string() << std::endl;
}

【讨论】:

  • 不能通过在第二个默认模板参数上使用enable_if 并返回void 来解决此问题吗?
  • 我花了一段时间才意识到typename enable_if 行是返回值。
  • @DyP:你在读我的想法,我只是在编辑答案;)
  • @remyabel:可以理解。实际上,您可以避免将T 指定为std::enable_if 的第二个模板参数,这会使print() 的返回类型为void()。我编辑了答案以提及这一点。
  • 不应该是no test( ... );吗?