【发布时间】:2020-12-19 15:11:23
【问题描述】:
我有一些代码在 g++ 10.2.0 下编译和运行良好,但被 clang++ 11.0.0 拒绝。
这是该问题的最小复制器:
#include <tuple>
#include <cstdint>
struct Dummy { };
using second_t = Dummy;
using example_t = std::tuple<size_t, second_t[8]>;
example_t f() {
example_t result;
return result;
}
int main() {
auto x = f();
(void) x;
}
在 g++ 中,它编译时没有任何抱怨,但使用 clang++ 我得到:
clang++ --std=c++20 -Wall -Werror main.cpp -o example
In file included from main.cpp:1:
/usr/bin/../lib/gcc/aarch64-unknown-linux-gnu/10.2.0/../../../../include/c++/10.2.0/tuple:137:4: error: array initializer must be an initializer list
: _M_head_impl(std::forward<_UHead>(__h)) { }
^
/usr/bin/../lib/gcc/aarch64-unknown-linux-gnu/10.2.0/../../../../include/c++/10.2.0/tuple:375:9: note: in instantiation of function template specialization 'std::_Head_base<1, Dummy [8], false>::_Head_base<Dummy [8]>' requested here
: _Base(std::forward<_Head>(_M_head(__in))) { }
^
/usr/bin/../lib/gcc/aarch64-unknown-linux-gnu/10.2.0/../../../../include/c++/10.2.0/tuple:236:9: note: in instantiation of member function 'std::_Tuple_impl<1, Dummy [8]>::_Tuple_impl' requested here
: _Inherited(std::move(_M_tail(__in))),
^
/usr/bin/../lib/gcc/aarch64-unknown-linux-gnu/10.2.0/../../../../include/c++/10.2.0/tuple:996:17: note: in instantiation of member function 'std::_Tuple_impl<0, unsigned long, Dummy [8]>::_Tuple_impl' requested here
constexpr tuple(tuple&&) = default;
^
main.cpp:11:12: note: in defaulted move constructor for 'std::tuple<unsigned long, Dummy [8]>' first required here
return result;
^
1 error generated.
make: *** [Makefile:2: all] Error 1
有趣的是,如果我将 second_t 更改为 int32_t,我也会收到来自 g++ 的错误:
g++ --std=c++20 -Wall -Werror main.cpp -o example
In file included from main.cpp:1:
/usr/include/c++/10.2.0/tuple: In instantiation of ‘constexpr std::_Head_base<_Idx, _Head, false>::_Head_base(_UHead&&) [with _UHead = int [8]; long unsigned int _Idx = 1; _Head = int [8]]’:
/usr/include/c++/10.2.0/tuple:375:49: required from ‘constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl(std::_Tuple_impl<_Idx, _Head>&&) [with long unsigned int _Idx = 1; _Head = int [8]]’
/usr/include/c++/10.2.0/tuple:237:42: required from ‘constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(std::_Tuple_impl<_Idx, _Head, _Tail ...>&&) [with long unsigned int _Idx = 0; _Head = long unsigned int; _Tail = {int [8]}]’
/usr/include/c++/10.2.0/tuple:996:17: required from here
/usr/include/c++/10.2.0/tuple:137:42: error: array used as initializer
137 | : _M_head_impl(std::forward<_UHead>(__h)) { }
| ^
make: *** [Makefile:2: all] Error 1
我猜这取决于使用 C 数组。如果我将代码更改为使用std::array,问题就会消失。但是,出于好奇,我仍然很好奇是否有办法用 C-array 做到这一点。
所以我的问题是:
- g++ 接受此代码有误吗?
- 有什么方法可以在像这样适用于 g++ 和 clang++ 的元组中返回 C 数组?
【问题讨论】:
-
根据 C++ 标准,在一个元组中有一个普通数组是否符合规定是一个很好的问题。然而,无论答案是什么:这只是其中一件不可取的事情,即使它是正确的,我也不会这样做。如果您需要元组中的数组,请使用
std::array而不是普通数组。您不会注意到有什么不同,需要处理的意外惊喜也会减少,头痛也会减少。 -
@SamVarshavchik 虽然我同意,但我不能这样做,根据我对问题中 std::array 的评论:)
-
std::array字面意思是template <typename T, std::size_t N> struct array { functions; private: T data[N]; };。如果数组对您来说是安全的,那么std::array也是如此 -
我认为这是因为对于元组的复制/移动构造函数:
std::is_move_constructible<Ti>::value must be true for all i, otherwise the behavior is undefined。对于 C 数组,这是错误的,所以我们得到未定义的行为 -
如果您不喜欢
std::array,您可以随时使用struct scared_of_the_STL { second_t data[8]; };。这在 C 中很常见,具有易于复制的数组。