使用包含不完整类型的 `std::vector` 递归定义和访问 `boost::variant` - libstdc++ 与 libc++
Recursively defining and visiting a `boost::variant` using an `std::vector` containing an incomplete type - libstdc++ vs libc++
我正在尝试定义和访问 "recursive" boost::variant
using an incomplete wrapper class and std::vector
作为我的间接技术。我的实现适用于 libstdc++,但不适用于 libc++.
这是我定义变体的方式:
struct my_variant_wrapper;
using my_variant_array = std::vector<my_variant_wrapper>; // <- indirection here
using my_variant = boost::variant<int, my_variant_array>;
struct my_variant_wrapper
{
my_variant _v;
template <typename... Ts>
my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { }
};
我正在使用 std::vector
引入间接 (这样动态分配将防止 my_variant
具有无限大小)。
我非常有信心我可以使用 std::vector<my_variant_wrapper>
,其中 my_variant_wrapper
是 incomplete type, because of paper N4510 ("Minimal incomplete type support for standard containers"):
然后我访问变体如下:
struct my_visitor
{
void operator()(int x) const { }
void operator()(const my_variant_array& arr) const
{
for(const auto& x : arr)
boost::apply_visitor(*this, x._v);
}
};
int main()
{
my_variant v0 = my_variant_array{
my_variant{1}, my_variant{2}, my_variant_array{
my_variant{3}, my_variant{4}
}
};
boost::apply_visitor(my_visitor{}, v0);
}
A minimal complete example is available on coliru.
我正在使用以下标志:
-std=c++1z -Wall -Wextra -Wpedantic
BOOST_VERSION
计算结果为 106100
.
代码:
按预期编译和运行:
g++ (测试版本:6.1 和 7),与 libstdc++.
clang++ (测试版本: 3.8), with libstdc++.
(作为奖励,通过进行适当的更改,它也可以与 std::variant
一起使用!)
编译失败:
- clang++ (测试版本: 3.8, 4), with libc++.
这是我在使用 libc++ 在 clang++ 上编译时遇到的错误:
In file included from prog.cc:2:
In file included from /usr/local/boost-1.61.0/include/boost/variant.hpp:17:
/usr/local/boost-1.61.0/include/boost/variant/variant.hpp:1537:28: error: no matching member function for call to 'initialize'
initializer::initialize(
~~~~~~~~~~~~~^~~~~~~~~~
/usr/local/boost-1.61.0/include/boost/variant/variant.hpp:1692:9: note: in instantiation of function template specialization 'boost::variant<int, std::__1::vector<my_variant_wrapper, std::__1::allocator<my_variant_wrapper> > >::convert_construct<my_variant_wrapper>' requested here
convert_construct(operand, 1L);
^
prog.cc:15:38: note: in instantiation of function template specialization 'boost::variant<int, std::__1::vector<my_variant_wrapper, std::__1::allocator<my_variant_wrapper> > >::variant<my_variant_wrapper>' requested here
my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { }
^
/usr/local/libcxx-head/include/c++/v1/memory:1783:31: note: in instantiation of function template specialization 'my_variant_wrapper::my_variant_wrapper<my_variant_wrapper &>' requested here
::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
^
/usr/local/libcxx-head/include/c++/v1/memory:1694:18: note: in instantiation of function template specialization 'std::__1::allocator<my_variant_wrapper>::construct<my_variant_wrapper, my_variant_wrapper &>' requested here
{__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
^
...
The full error is available on wandbox.
为什么代码没有用 libc++ 编译? (这可能是 libc++ 的 N4510 实现中的缺陷需要报告吗? )
该错误似乎表明该变体未能检测到应初始化哪些成员,但老实说我无法理解它。我也对使用 libstdc++ (具有相同的提升版本) 按预期工作这一事实感到困惑。
我在回溯中看到了这个:
note: in instantiation of function template specialization
'my_variant_wrapper::my_variant_wrapper<my_variant_wrapper &>
'
requested here
这清楚地表明您的构造函数模板正在劫持复制构造函数。
约束它,你的问题就消失了。
实现之间的差异是由于 vector
的复制构造函数复制元素的方式。 libstdc++ 将源元素视为 const
:
vector(const vector& __x)
: _Base(__x.size(),
_Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
{
this->_M_impl._M_finish =
std::__uninitialized_copy_a(__x.begin(), __x.end(),
this->_M_impl._M_start,
_M_get_Tp_allocator());
}
因为 begin()
和 end()
在 const vector& x
上被调用,它们 return 常量迭代器。
libc++ 将源元素视为非const
:
template <class _Tp, class _Allocator>
vector<_Tp, _Allocator>::vector(const vector& __x)
: __base(__alloc_traits::select_on_container_copy_construction(__x.__alloc()))
{
#if _LIBCPP_DEBUG_LEVEL >= 2
__get_db()->__insert_c(this);
#endif
size_type __n = __x.size();
if (__n > 0)
{
allocate(__n);
__construct_at_end(__x.__begin_, __x.__end_, __n);
}
}
__begin_
和 __end_
是 pointer
s,并且由于 const
是浅的,__x
的 const
-ness 不是t 让指针 const
.
两者都是一致的,因为 CopyInsertable
需要来自 const
和非 const
来源的可复制性。但是,您的模板仅劫持来自非 const
的复制(因为它丢失了 template/non-template 决胜局从 const
复制的情况),因此您只在 libc++ 中看到问题。
我正在尝试定义和访问 "recursive" boost::variant
using an incomplete wrapper class and std::vector
作为我的间接技术。我的实现适用于 libstdc++,但不适用于 libc++.
这是我定义变体的方式:
struct my_variant_wrapper;
using my_variant_array = std::vector<my_variant_wrapper>; // <- indirection here
using my_variant = boost::variant<int, my_variant_array>;
struct my_variant_wrapper
{
my_variant _v;
template <typename... Ts>
my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { }
};
我正在使用 std::vector
引入间接 (这样动态分配将防止 my_variant
具有无限大小)。
我非常有信心我可以使用 std::vector<my_variant_wrapper>
,其中 my_variant_wrapper
是 incomplete type, because of paper N4510 ("Minimal incomplete type support for standard containers"):
然后我访问变体如下:
struct my_visitor
{
void operator()(int x) const { }
void operator()(const my_variant_array& arr) const
{
for(const auto& x : arr)
boost::apply_visitor(*this, x._v);
}
};
int main()
{
my_variant v0 = my_variant_array{
my_variant{1}, my_variant{2}, my_variant_array{
my_variant{3}, my_variant{4}
}
};
boost::apply_visitor(my_visitor{}, v0);
}
A minimal complete example is available on coliru.
我正在使用以下标志:
-std=c++1z -Wall -Wextra -Wpedantic
BOOST_VERSION
计算结果为106100
.
代码:
按预期编译和运行:
g++ (测试版本:6.1 和 7),与 libstdc++.
clang++ (测试版本: 3.8), with libstdc++.
(作为奖励,通过进行适当的更改,它也可以与
std::variant
一起使用!)
编译失败:
- clang++ (测试版本: 3.8, 4), with libc++.
这是我在使用 libc++ 在 clang++ 上编译时遇到的错误:
In file included from prog.cc:2:
In file included from /usr/local/boost-1.61.0/include/boost/variant.hpp:17:
/usr/local/boost-1.61.0/include/boost/variant/variant.hpp:1537:28: error: no matching member function for call to 'initialize'
initializer::initialize(
~~~~~~~~~~~~~^~~~~~~~~~
/usr/local/boost-1.61.0/include/boost/variant/variant.hpp:1692:9: note: in instantiation of function template specialization 'boost::variant<int, std::__1::vector<my_variant_wrapper, std::__1::allocator<my_variant_wrapper> > >::convert_construct<my_variant_wrapper>' requested here
convert_construct(operand, 1L);
^
prog.cc:15:38: note: in instantiation of function template specialization 'boost::variant<int, std::__1::vector<my_variant_wrapper, std::__1::allocator<my_variant_wrapper> > >::variant<my_variant_wrapper>' requested here
my_variant_wrapper(Ts&&... xs) : _v(std::forward<Ts>(xs)...) { }
^
/usr/local/libcxx-head/include/c++/v1/memory:1783:31: note: in instantiation of function template specialization 'my_variant_wrapper::my_variant_wrapper<my_variant_wrapper &>' requested here
::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
^
/usr/local/libcxx-head/include/c++/v1/memory:1694:18: note: in instantiation of function template specialization 'std::__1::allocator<my_variant_wrapper>::construct<my_variant_wrapper, my_variant_wrapper &>' requested here
{__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
^
...
The full error is available on wandbox.
为什么代码没有用 libc++ 编译? (这可能是 libc++ 的 N4510 实现中的缺陷需要报告吗? )
该错误似乎表明该变体未能检测到应初始化哪些成员,但老实说我无法理解它。我也对使用 libstdc++ (具有相同的提升版本) 按预期工作这一事实感到困惑。
我在回溯中看到了这个:
note: in instantiation of function template specialization '
my_variant_wrapper::my_variant_wrapper<my_variant_wrapper &>
' requested here
这清楚地表明您的构造函数模板正在劫持复制构造函数。
约束它,你的问题就消失了。
实现之间的差异是由于 vector
的复制构造函数复制元素的方式。 libstdc++ 将源元素视为 const
:
vector(const vector& __x)
: _Base(__x.size(),
_Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
{
this->_M_impl._M_finish =
std::__uninitialized_copy_a(__x.begin(), __x.end(),
this->_M_impl._M_start,
_M_get_Tp_allocator());
}
因为 begin()
和 end()
在 const vector& x
上被调用,它们 return 常量迭代器。
libc++ 将源元素视为非const
:
template <class _Tp, class _Allocator>
vector<_Tp, _Allocator>::vector(const vector& __x)
: __base(__alloc_traits::select_on_container_copy_construction(__x.__alloc()))
{
#if _LIBCPP_DEBUG_LEVEL >= 2
__get_db()->__insert_c(this);
#endif
size_type __n = __x.size();
if (__n > 0)
{
allocate(__n);
__construct_at_end(__x.__begin_, __x.__end_, __n);
}
}
__begin_
和 __end_
是 pointer
s,并且由于 const
是浅的,__x
的 const
-ness 不是t 让指针 const
.
两者都是一致的,因为 CopyInsertable
需要来自 const
和非 const
来源的可复制性。但是,您的模板仅劫持来自非 const
的复制(因为它丢失了 template/non-template 决胜局从 const
复制的情况),因此您只在 libc++ 中看到问题。