【发布时间】:2021-02-18 17:10:41
【问题描述】:
这是一个非常奇怪的错误,我不明白为什么会这样。
我正在通过编写一些通用 I/O 读取器/写入器实现来练习 SFINAE 概念。在我的本地机器上,一切正常,没有任何分段错误。但是,当我尝试在Wandbox 或其他一些在线编译器站点上测试相同的代码时,我收到SIGABRT、Segmentation fault 错误和Runtime error: exit code is 2147483647 之类的东西。我在本地机器上通过 gdb 运行我的代码,我没有收到任何信号或分段错误。我确保我使用的所有编译标志在我的本地设备和在线编译器上都是相同的。我已经使用 GCC 9.2.0 和 GCC 10.2.0 进行了测试,但在这两种情况下,只有在线编译器会出现问题。
我正在测试导致分段错误的代码:
#include <iostream>
#include <vector>
#include <tuple>
#include <utility>
#include <type_traits>
namespace io::utility {
template <typename T, typename = void>
struct is_istream_streamble
: std::false_type
{ };
template <typename T>
struct is_istream_streamble <T, std::void_t <decltype(std::cin >> std::declval <T&> ())>>
: std::true_type
{ };
template <typename T>
constexpr bool is_istream_streamble_v = is_istream_streamble <T>::value;
template <typename T, typename = void>
struct is_ostream_streamble
: std::false_type
{ };
template <typename T>
struct is_ostream_streamble <T, std::void_t <decltype(std::cerr << std::declval <T> ())>>
: std::true_type
{ };
template <typename T>
constexpr bool is_ostream_streamble_v = is_ostream_streamble <T>::value;
template <typename T, typename = void>
struct has_begin_iterator
: std::false_type
{ };
template <typename T>
struct has_begin_iterator <T, std::void_t <decltype(std::declval <T> ().begin())>>
: std::true_type
{ };
template <typename T>
constexpr bool has_begin_iterator_v = has_begin_iterator <T>::value;
template <typename T, typename = void>
struct has_end_iterator
: std::false_type
{ };
template <typename T>
struct has_end_iterator <T, std::void_t <decltype(std::declval <T> ().end())>>
: std::true_type
{ };
template <typename T>
constexpr bool has_end_iterator_v = has_end_iterator <T>::value;
}
namespace io {
class reader {
private:
std::istream& stream;
public:
reader (std::istream& stream = std::cin) : stream (stream) { }
template <typename T>
std::enable_if_t <utility::is_ostream_streamble_v <T>, reader&>
operator () (T& object)
{ return stream >> object, *this; }
template <typename T>
std::enable_if_t <!utility::is_ostream_streamble_v <T> && utility::has_begin_iterator_v <T> && utility::has_end_iterator_v <T>, reader&>
operator () (T& object) {
for (auto& element : object)
(*this)(element);
return *this;
}
template <typename A, typename B>
reader& operator () (std::pair <A, B>& pair)
{ return (*this)(pair.first, pair.second); }
template <size_t N, typename...T>
std::enable_if_t <(sizeof...(T) <= N), reader&>
operator () (std::tuple <T...>&)
{ return *this; }
template <size_t N, typename...T>
std::enable_if_t <(sizeof...(T) > N), reader&>
operator () (std::tuple <T...>& tuple)
{ return (*this)(std::get <N> (tuple)), operator () <N + 1, T...> (tuple); }
template <typename...T>
reader& operator () (std::tuple <T...>& tuple)
{ return operator () <0, T...> (tuple); }
reader& operator () ()
{ return *this; }
template <typename A, typename...B>
reader& operator () (A& object, B&...objects)
{ return (*this)(object)(objects...); }
};
class writer {
private:
std::ostream& stream;
public:
writer (std::ostream& stream = std::cerr) : stream (stream) { }
template <typename T>
std::enable_if_t <utility::is_ostream_streamble_v <T>, writer&>
operator () (const T& object)
{ return stream << object, *this; }
template <typename T>
std::enable_if_t <!utility::is_ostream_streamble_v <T> && utility::has_begin_iterator_v <T> && utility::has_end_iterator_v <T>, writer&>
operator () (const T& object) {
for (const auto& element : object)
(*this)(element, ' ');
return *this;
}
template <typename A, typename B>
writer& operator () (const std::pair <A, B>& pair)
{ return (*this)(pair.first, " ", pair.second); }
template <size_t N, typename...T>
std::enable_if_t <(sizeof...(T) <= N), writer&>
operator () (const std::tuple <T...>&)
{ return *this; }
template <size_t N, typename...T>
std::enable_if_t <(sizeof...(T) > N), writer&>
operator () (const std::tuple <T...>& tuple)
{ return (*this)(std::get <N> (tuple), ' '), operator () <N + 1, T...> (tuple); }
template <typename...T>
writer& operator () (const std::tuple <T...>& tuple)
{ return operator () <0, T...> (tuple); }
writer& operator () ()
{ return *this; }
template <typename A, typename...B>
writer& operator () (const A& object, const B&...objects)
{ return (*this)(object)(objects...); }
};
}
using reader = io::reader;
using writer = io::writer;
reader read (std::cin);
writer write (std::cout);
auto main () -> int32_t {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.precision(10);
std::cerr.precision(10);
std::cout << std::fixed << std::boolalpha;
std::cerr << std::fixed << std::boolalpha;
int n;
read(n);
std::vector <int> a (n);
read(a);
write(a);
return 0;
}
令人惊讶的是,只需删除 std::ios::sync_with_stdio(false); 就可以使一切顺利进行,在线编译器上不会出现任何错误。我花了一个小时尝试愚蠢的事情来弄清楚这一点。但我不明白为什么会这样。保持 stdio 同步如何解决我的问题?非常感谢您的解释。
【问题讨论】:
-
你给你的程序提供什么输入?调试器显示什么?请添加所有需要的
#includes - 猜测它们并不好玩。auto main () -> int32_t?为什么int32_t? -
未定义行为的一个有趣的方面是,对代码进行看似无关的更改会导致症状消失。问题不太可能出现在
sync_with_stdio(false)电话中。然而,删除调用很可能会改变整个程序的内存组织 - 并且未定义行为的症状也会改变。 -
那个需要引用。我找不到任何reference to that style。您的返回类型与预期不符,我不确定这与传统的
int main()相比有什么优势,尤其是考虑到它更加冗长。有那么一刻,我以为这是 Rust,但它是 C++。 -
@LostArrow -
main()需要返回int。int不需要是int32_t。 -
auto main () -> int32_t是错误的。语言要求它是int main()或int main(int argc, char** argv)(尽管您可以根据需要更改参数名称)。
标签: c++ segmentation-fault c++17