I'm trying to move the whole vector to another variable.
The following code block does not compile.
I thought that the call to std::move would force to use the Move-Constructor. Instead it seems like std::vector is using the Copy-Constructor that is deleted.
What's wrong here? How can I move all elements without copying them?
#include <vector>
class TheClass {
public:
TheClass(const TheClass&) = delete;
TheClass(TheClass&&) = default;
};
void fun2(const std::vector<TheClass> data) {
std::vector<TheClass> a = std::move(data);
a.size();
}
leads to:
clang++ -o /cplayground/cplayground /cplayground/code.cpp -I/cplayground/include -L/cplayground/lib -std=c++20 -O0 -Wall -no-pie -lm -pthread
In file included from /cplayground/code.cpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/vector:66:
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_uninitialized.h:137:7: error: static_assert failed due to requirement 'is_constructible<TheClass, const TheClass &>::value' "result type must be constructible from value type of input range"
static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_uninitialized.h:325:19: note: in instantiation of function template specialization 'std::uninitialized_copy<__gnu_cxx::__normal_iterator<const TheClass *, std::vector<TheClass, std::allocator<TheClass> > >, TheClass *>' requested here
{ return std::uninitialized_copy(__first, __last, __result); }
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_vector.h:558:9: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator<const TheClass *, std::vector<TheClass, std::allocator<TheClass> > >, TheClass *, TheClass>' requested here
std::__uninitialized_copy_a(__x.begin(), __x.end(),
^
/cplayground/code.cpp:10:29: note: in instantiation of member function 'std::vector<TheClass, std::allocator<TheClass> >::vector' requested here
std::vector<TheClass> a = std::move(data);
^
In file included from /cplayground/code.cpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/vector:62:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_algo.h:62:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_tempbuf.h:60:
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_construct.h:109:38: error: call to deleted constructor of 'TheClass'
{ ::new(static_cast<void*>(__p)) _Tp(std::forward<_Args>(__args)...); }
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_uninitialized.h:91:8: note: in instantiation of function template specialization 'std::_Construct<TheClass, const TheClass &>' requested here
std::_Construct(std::__addressof(*__cur), *__first);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_uninitialized.h:150:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<__gnu_cxx::__normal_iterator<const TheClass *, std::vector<TheClass, std::allocator<TheClass> > >, TheClass *>' requested here
__uninit_copy(__first, __last, __result);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_uninitialized.h:325:19: note: in instantiation of function template specialization 'std::uninitialized_copy<__gnu_cxx::__normal_iterator<const TheClass *, std::vector<TheClass, std::allocator<TheClass> > >, TheClass *>' requested here
{ return std::uninitialized_copy(__first, __last, __result); }
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_vector.h:558:9: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator<const TheClass *, std::vector<TheClass, std::allocator<TheClass> > >, TheClass *, TheClass>' requested here
std::__uninitialized_copy_a(__x.begin(), __x.end(),
^
/cplayground/code.cpp:10:29: note: in instantiation of member function 'std::vector<TheClass, std::allocator<TheClass> >::vector' requested here
std::vector<TheClass> a = std::move(data);
^
/cplayground/code.cpp:5:3: note: 'TheClass' has been explicitly marked deleted here
TheClass(const TheClass&) = delete;
^
2 errors generated.
You can't move something that is const (I mean, you can call std::move on it, but it won't allow you to move things). Remove that qualifier and (maybe) pass a rvalue reference:
void fun2(std::vector<TheClass>&& data)
{
std::vector<TheClass> a = std::move(data);
a.size();
}
Then :
std::vector<TheClass> vec;
fun2(std::move(vec));
Note that you may omit that &&(see https://godbolt.org/z/5M1f4j66r).
I realize nested std::any's are a bad idea. Nevertheless, I encountered something that's making me scratch my head and I'm just trying to understand why the compiler is choking. Consider the following line of code (assuming that the arg variable is a std::any containing another std::any):
auto result = std::any_cast<std::any>(arg);
That line compiles just fine. Now, consider this line, where I have a std::any containing a std::tuple that contains another std::any:
auto result = std::any_cast<std::tuple<std::any>>(arg);
Now, the compiler blows up. These are the errors I get:
In file included from /home/james/git/trogdor-pp/src/core/event/triggers/luaeventtrigger.cpp:1:
In file included from /home/james/git/trogdor-pp/src/core/include/trogdor/game.h:5:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:37:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/new:40:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/exception:144:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/nested_exception.h:40:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/move.h:55:
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:132:31: error: no member named 'value' in 'std::is_copy_constructible<std::tuple<std::any> >'
: public conditional<_B1::value, _B2, _B1>::type
~~~~~^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:170:17: note: in instantiation of template class 'std::__and_<std::is_copy_constructible<std::tuple<std::any> >, std::is_constructible<std::tuple<std::any>, const std::tuple<std::any> &> >' requested here
enable_if<__and_<is_copy_constructible<_Tp>,
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:175:5: note: in instantiation of template type alias '__any_constructible' requested here
using __any_constructible_t =
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:181:56: note: in instantiation of template type alias '__any_constructible_t' requested here
__any_constructible_t<_Tp, _ValueType&&> = true,
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:183:7: note: while substituting prior template arguments into non-type template parameter [with _ValueType = const std::tuple<std::any> &, _Tp = std::tuple<std::any>, _Mgr = std::any::_Manager_external<std::tuple<std::any> >]
any(_ValueType&& __value)
^~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:884:56: note: while substituting deduced template arguments into function template 'any' [with _ValueType = const std::tuple<std::any> &, _Tp = (no value), _Mgr = (no value), $3 = (no value), $4 = (no value)]
: public __bool_constant<__is_constructible(_Tp, _Args...)>
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:884:32: note: (skipping 2 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
: public __bool_constant<__is_constructible(_Tp, _Args...)>
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:908:14: note: in instantiation of template class 'std::__is_copy_constructible_impl<std::tuple<std::any>, true>' requested here
: public __is_copy_constructible_impl<_Tp>
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:109:14: note: in instantiation of template class 'std::is_copy_constructible<std::tuple<std::any> >' requested here
: public conditional<_B1::value, _B1, _B2>::type
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:358:16: note: in instantiation of template class 'std::__or_<std::is_reference<std::tuple<std::any> >, std::is_copy_constructible<std::tuple<std::any> > >' requested here
{ return __or_<is_reference<_Tp>, is_copy_constructible<_Tp>>::value; }
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:481:26: note: in instantiation of function template specialization 'std::any::__is_valid_cast<std::tuple<std::any> >' requested here
static_assert(any::__is_valid_cast<_ValueType>(),
^
/home/james/git/trogdor-pp/src/core/event/triggers/luaeventtrigger.cpp:42:32: note: in instantiation of function template specialization 'std::any_cast<std::tuple<std::any> >' requested here
auto arg = std::any_cast<std::tuple<std::any>>(a);
^
In file included from /home/james/git/trogdor-pp/src/core/event/triggers/luaeventtrigger.cpp:1:
In file included from /home/james/git/trogdor-pp/src/core/include/trogdor/game.h:5:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:37:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/new:40:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/exception:144:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/nested_exception.h:40:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/move.h:55:
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:137:31: error: no member named 'value' in 'std::is_copy_constructible<std::tuple<std::any> >'
: public conditional<_B1::value, __and_<_B2, _B3, _Bn...>, _B1>::type
~~~~~^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:150:37: note: in instantiation of template class 'std::__and_<std::is_copy_constructible<std::tuple<std::any> >, std::__not_<std::is_constructible<std::tuple<std::any>, const std::tuple<std::any> &> >, std::__not_<std::__is_in_place_type<std::tuple<std::any> > > >' requested here
inline constexpr bool __and_v = __and_<_Bn...>::value;
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:192:27: note: in instantiation of variable template specialization 'std::__and_v<std::is_copy_constructible<std::tuple<std::any> >, std::__not_<std::is_constructible<std::tuple<std::any>, const std::tuple<std::any> &> >, std::__not_<std::__is_in_place_type<std::tuple<std::any> > > >' requested here
enable_if_t<__and_v<is_copy_constructible<_Tp>,
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:196:7: note: while substituting prior template arguments into non-type template parameter [with _ValueType = const std::tuple<std::any> &, _Tp = std::tuple<std::any>, _Mgr = std::any::_Manager_external<std::tuple<std::any> >]
any(_ValueType&& __value)
^~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:884:56: note: while substituting deduced template arguments into function template 'any' [with _ValueType = const std::tuple<std::any> &, _Tp = (no value), _Mgr = (no value), $3 = (no value)]
: public __bool_constant<__is_constructible(_Tp, _Args...)>
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:884:32: note: while substituting deduced template arguments into function template 'tuple' [with _Dummy = (no value), $1 = (no value)]
: public __bool_constant<__is_constructible(_Tp, _Args...)>
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:902:14: note: (skipping 1 context in backtrace; use -ftemplate-backtrace-limit=0 to see all)
: public is_constructible<_Tp, const _Tp&>
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:908:14: note: in instantiation of template class 'std::__is_copy_constructible_impl<std::tuple<std::any>, true>' requested here
: public __is_copy_constructible_impl<_Tp>
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/type_traits:109:14: note: in instantiation of template class 'std::is_copy_constructible<std::tuple<std::any> >' requested here
: public conditional<_B1::value, _B1, _B2>::type
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:358:16: note: in instantiation of template class 'std::__or_<std::is_reference<std::tuple<std::any> >, std::is_copy_constructible<std::tuple<std::any> > >' requested here
{ return __or_<is_reference<_Tp>, is_copy_constructible<_Tp>>::value; }
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/any:481:26: note: in instantiation of function template specialization 'std::any::__is_valid_cast<std::tuple<std::any> >' requested here
static_assert(any::__is_valid_cast<_ValueType>(),
^
/home/james/git/trogdor-pp/src/core/event/triggers/luaeventtrigger.cpp:42:32: note: in instantiation of function template specialization 'std::any_cast<std::tuple<std::any> >' requested here
auto arg = std::any_cast<std::tuple<std::any>>(a);
I'm having a hard time unpacking all of that and figuring out what's wrong and how I can fix it.
EDIT: we've discovered in the comments that this code does not compile under G++ 9 and below, but compiles on G++ 10 and above, and also fails on clang 10 and below, but compiles on clang 11 and above. Does anybody have any guesses as to why this might be the case?
The key part is error: no member named 'value' in 'std::is_copy_constructible<std::tuple<std::any> >'
This is the compiler's way of complaining that the indicated object, the std::tuple, is not copy-constructible.
Your gcc does not believe, apparently, that std::tuplestd::any is copy-constructible. A tuple, in of itself, should be copy constructible if everything in the tuple is copy-constructible. Given that std::any is C++17, and gcc 9's C++17 support is incomplete, at least incomplete insofar as this part of C++17 goes (gcc 11's feature list still includes some C++17 features).
std::reference_wrapper should be copy-constructible, regardless of the contained type (it's just a pointer inside). However, when asserting that using
static_assert(std::is_copy_constructible<std::reference_wrapper<std::any> >::value, "!");
Clang terminates with a compile error from inside the STL, while GCC reports no problems. This is not the case with other types (I tested std::reference_wrapper<int> and std::any directly).
I have an example showing compilation in GCC and Clang at https://godbolt.org/z/7YwkWb.
As far as I understand it, even if the type given to std::is_copy_constructible is not copy-constructible, it should just give a compile-time false, rather than failing compilation.
What is the problem here? Is that a compiler bug or am I missing something?
The compile error from Clang is:
In file included from <source>:1:
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/type_traits:132:31: error: no member named 'value' in 'std::is_copy_constructible<std::reference_wrapper<std::any> >'
: public conditional<_B1::value, _B2, _B1>::type
~~~~~^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/any:170:17: note: in instantiation of template class 'std::__and_<std::is_copy_constructible<std::reference_wrapper<std::any> >, std::is_constructible<std::reference_wrapper<std::any>, const std::reference_wrapper<std::any> &> >' requested here
enable_if<__and_<is_copy_constructible<_Tp>,
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/any:175:5: note: in instantiation of template type alias '__any_constructible' requested here
using __any_constructible_t =
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/any:181:56: note: in instantiation of template type alias '__any_constructible_t' requested here
__any_constructible_t<_Tp, _ValueType&&> = true,
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/any:183:7: note: while substituting prior template arguments into non-type template parameter [with _ValueType = const std::reference_wrapper<std::any> &, _Tp = std::reference_wrapper<std::any>, _Mgr = std::any::_Manager_internal<std::reference_wrapper<std::any> >]
any(_ValueType&& __value)
^~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/type_traits:921:56: note: while substituting deduced template arguments into function template 'any' [with _ValueType = const std::reference_wrapper<std::any> &, _Tp = (no value), _Mgr = (no value), $3 = (no value), $4 = (no value)]
: public __bool_constant<__is_constructible(_Tp, _Args...)>
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/type_traits:933:14: note: in instantiation of template class 'std::is_constructible<std::reference_wrapper<std::any>, const std::reference_wrapper<std::any> &>' requested here
: public is_constructible<_Tp, const _Tp&>
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/type_traits:939:14: note: in instantiation of template class 'std::__is_copy_constructible_impl<std::reference_wrapper<std::any>, true>' requested here
: public __is_copy_constructible_impl<_Tp>
^
<source>:12:20: note: in instantiation of template class 'std::is_copy_constructible<std::reference_wrapper<std::any> >' requested here
static_assert(std::is_copy_constructible<std::reference_wrapper<std::any> >::value, "!");
^
In file included from <source>:1:
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/type_traits:137:31: error: no member named 'value' in 'std::is_copy_constructible<std::reference_wrapper<std::any> >'
: public conditional<_B1::value, __and_<_B2, _B3, _Bn...>, _B1>::type
~~~~~^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/any:192:27: note: in instantiation of template class 'std::__and_<std::is_copy_constructible<std::reference_wrapper<std::any> >, std::__not_<std::is_constructible<std::reference_wrapper<std::any>, const std::reference_wrapper<std::any> &> >, std::__not_<std::__is_in_place_type<std::reference_wrapper<std::any> > > >' requested here
enable_if_t<__and_<is_copy_constructible<_Tp>,
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/any:196:7: note: while substituting prior template arguments into non-type template parameter [with _ValueType = const std::reference_wrapper<std::any> &, _Tp = std::reference_wrapper<std::any>, _Mgr = std::any::_Manager_internal<std::reference_wrapper<std::any> >]
any(_ValueType&& __value)
^~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/type_traits:921:56: note: while substituting deduced template arguments into function template 'any' [with _ValueType = const std::reference_wrapper<std::any> &, _Tp = (no value), _Mgr = (no value), $3 = (no value)]
: public __bool_constant<__is_constructible(_Tp, _Args...)>
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/type_traits:933:14: note: in instantiation of template class 'std::is_constructible<std::reference_wrapper<std::any>, const std::reference_wrapper<std::any> &>' requested here
: public is_constructible<_Tp, const _Tp&>
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/type_traits:939:14: note: in instantiation of template class 'std::__is_copy_constructible_impl<std::reference_wrapper<std::any>, true>' requested here
: public __is_copy_constructible_impl<_Tp>
^
<source>:12:20: note: in instantiation of template class 'std::is_copy_constructible<std::reference_wrapper<std::any> >' requested here
static_assert(std::is_copy_constructible<std::reference_wrapper<std::any> >::value, "!");
^
2 errors generated.
Compiler returned: 1
The error you see clearly states at the top that std::is_copy_constructible doesn't have a value member for the specialization you check. Now, the types you pass don't violate the contract of std::is_copy_constructible, so there is no risk of undefined behavior.
Which means only one thing, it's not a compiler issue, but a standard library implementation issue. Clang uses the default installed standard library implementation, which on the system godbolt is running is libstdc++ (the GNU implementation). This evidently causes the issue due to some possible soft incompatibility.
If you specify the -stdlib=libc++ option (libc++ is the LLVM implementation of the standard library) when building with Clang, then it accepts the code fine.
See it here.
The program is very mundane, like that:
#include <vector>
std::vector<int> bombs;
int main()
{
bombs.push_back(42); // compile errors
}
The diagnostics do not make much sense to me:
In file included from main.cpp:1:
In file included from
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/vector:60:
In file included from
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/bits/stl_algobase.h:64:
In file included from
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/bits/stl_pair.h:59:
In file included from
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/bits/move.h:55:
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/type_traits:1061:48: error: '_Tp' does not refer to a value
: public __bool_constant<__is_assignable(_Tp, _Up)>
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/type_traits:1059:21: note: declared here
template<typename _Tp, typename _Up>
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/type_traits:1061:53: error: _Up does not refer to a value
: public __bool_constant<__is_assignable(_Tp, _Up)>
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/type_traits:1059:35: note: declared here
template<typename _Tp, typename _Up>
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/type_traits:1062:5:
error: expected class name
{ };
^
In file included from main.cpp:1:
In file included from
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/vector:63:
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/bits/stl_uninitialized.h:128:70:
error: no member named value in std::is_assignable<int &, int &&>
const bool __assignable = is_assignable<_RefType2, _RefType1>::value;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/bits/stl_uninitialized.h:289:19:
note: in instantiation of function template specialization
std::uninitialized_copy<std::move_iterator<int *>, int *> requested
here
{ return std::uninitialized_copy(__first, __last, __result); }
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/bits/stl_uninitialized.h:310:19:
note: in instantiation of function template specialization
std::__uninitialized_copy_a<std::move_iterator<int *>, int *, int>
requested here
return std::__uninitialized_copy_a
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/bits/vector.tcc:446:13: note: in instantiation of function template specialization std::__uninitialized_move_if_noexcept_a<int *, int *, std::allocator<int> > requested here
= std::__uninitialized_move_if_noexcept_a
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/bits/vector.tcc:109:4:
note: in instantiation of function template specialization
std::vector<int, std::allocator<int> >::_M_realloc_insert<int>
requested here
_M_realloc_insert(end(), std::forward<_Args>(__args)...);
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../include/c++/8.1.0/bits/stl_vector.h:1091:9:
note: in instantiation of function template specialization
std::vector<int, std::allocator<int> >::emplace_back<int> requested
here
{ emplace_back(std::move(__x)); }
^
main.cpp:7:9: note: in instantiation of member function
std::vector<int, std::allocator<int> >::push_back requested here
bombs.push_back(42);
^
4 errors generated.
Same with g++ compiles without any issues. I can't really look at the
library headers that Clang at Coliru includes to program for to figure it out myself. Does anyone have any ideas what is wrong and how to overcome the issue?
I 100% wouldn't worry about this, it's just some weirdness at Coliru. It works perfectly at Wandbox, right back to Clang 3.1
According to the donations page, you can probably report this problem to [email address redacted].
Hello fellow C++ programmers!
I'm trying to write a C++ variant class to emulate a stictly-typed union without restrictions for MSVS2012 (which is lacking one from the standard). I know that there are lots of variant implementations for C++, but reinventing stuff is fun. And there is some academic interest in the underlaying problem, anyway.
Below is the code - it's a bit over-complicated to my taste due to two points:
That's for MSVC 2012 which lacks a number of most-crucial parts of C++11 standard for such a task, like variadic templates or constructor inheritance
I failed to came up with a way to make dedicated copy/move constructors without creating ambiguous overload with templated variants. I've tried to make a std::enable_if check to prevent templated constructors spawning for derived/same types as the variant itself, but it seems to prevent MSVC implicit conversion. So I've moved the check for derived class (variant itself copy/move, not an initialization by copy/move of one of the types) into helper structs.
The code below is somewhat shortened version without accessors (they are trivial anyway), some checks and namespaces, for the sake of less noise. The problem, which is explained in more detail below the code, isn't present on MSVC though (it works there surprisingly flawlessly), but happens with clang and libstdc++ STL library (the GNU implementation from gcc-4.9.2). And only if I'm trying to put unique_ptr's within the Variant and place it into the std::vector.
#include <memory>
#include <vector>
#include <utility>
// A number of template tricks to emulate tuple query things still missing from MSVC2012. Omitted for clarity, nothing really special there - looping through tuples and checking types with is_conversible
#include "core_template_magic.hpp"
template <typename TypesTuple>
class Variant
{
public:
typedef Variant<TypesTuple> VariantBase;
template <typename T>
struct type_of
{
enum { value = find_type_in_tuple<T, TypesTuple>::value };
static_assert(value >= 0, "There is no such type in this variant");
};
public:
template <typename T>
Variant(const T& value) :
type_(get_constructor_type<T>::run(value))
{
construct_copy<T, 0>::run(mem_, value);
}
template <typename T>
Variant(T&& value) :
type_(get_constructor_type<T>::run(value))
{
construct_move<T, 0>::run(mem_, std::forward<T>(value));
}
~Variant()
{
destroy<0>::run(mem_, type_);
}
private:
/* a wrapper around type_of to work around copy constructors */
template <typename T>
struct is_self : public std::is_base_of<VariantBase, T> {};
template <typename T, bool is_self = is_self<T>::value>
struct get_constructor_type {};
template <typename T>
struct get_constructor_type<T, false>
{
static int run(const T&)
{
return type_of<T>::value;
}
static int run(T&&)
{
return type_of<T>::value;
}
};
template <typename T>
struct get_constructor_type<T, true>
{
static int run(const VariantBase& other)
{
return other.type_;
}
static int run(VariantBase&& other)
{
return other.type_;
}
};
/* a helper for the copy constructor */
template <typename T, int i,
int count = std::tuple_size<TypesTuple>::value,
bool is_self = is_self<T>::value>
struct construct_copy
{
static void run(char* mem, const T& other)
{
new (mem) T(other);
}
};
// If that's an another variant of same type - find out it's type
// and call an appropriate copy constructor
template <typename T, int i, int count>
struct construct_copy<T, i, count, true>
{
static void run(char* mem, const VariantBase& other)
{
typedef typename std::tuple_element<i, TypesTuple>::type E;
if (other.type_ == i)
new (mem) E(*reinterpret_cast<const E*>(other.mem_));
else
construct_copy<T, i + 1, count, true>::run(mem, other);
}
};
template <typename T, int count>
struct construct_copy<T, count, count, true>
{ static void run(char* mem, const VariantBase& other) {} };
/* a helper for the move constructor */
template <typename T, int i,
int count = std::tuple_size<TypesTuple>::value,
bool is_self = is_self<T>::value>
struct construct_move
{
static void run(char* mem, T&& other)
{
new (mem) T(std::forward<T>(other));
}
};
// If that's an another variant of same type - find out it's type
// and call an appropriate move constructor
template <typename T, int i, int count>
struct construct_move<T, i, count, true>
{
static void run(char* mem, VariantBase&& other)
{
typedef typename std::tuple_element<i, TypesTuple>::type E;
if (other.type_ == i)
new (mem) E(std::move(*reinterpret_cast<E*>(other.mem_)));
else
construct_move<T, i + 1, count, true>::run(mem, std::forward<VariantBase>(other));
}
};
template <typename T, int count>
struct construct_move<T, count, count, true>
{ static void run(char* mem, VariantBase&& other) {} };
// A destructor helper
template <int i, int count = std::tuple_size<TypesTuple>::value>
struct destroy
{
static void run(char* mem, const int type)
{
typedef typename std::tuple_element<i, TypesTuple>::type T;
if (type == i)
reinterpret_cast<T*>(mem)->~T();
else
destroy<i + 1, count>::run(mem, type);
}
};
template <int count>
struct destroy<count, count>
{ static void run(char* mem, const int type) {} };
private:
const int type_;
char mem_[get_max_sizeof_of_types<TypesTuple>::value];
};
// example problematic usage
struct Test : public Variant<std::tuple<
std::unique_ptr<int>,
std::unique_ptr<bool>,
std::unique_ptr<double>
>>
{
using VariantBase::VariantBase;
};
int main(int argc, char *argv[])
{
std::vector<Test> v;
v.emplace_back(std::move(std::unique_ptr<int>(new int)));
return 0;
}
So, when I'm trying to compile that on clang++-3.6.0 with libstdc++-4.9.2, I'm getting the error
../main.cpp:58:14: error: multiple overloads of 'run' instantiate to the same signature 'int (Test &&)'
static int run(T&&)
^
../main.cpp:31:9: note: in instantiation of template class 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::get_constructor_type<Test &, false>' requested here
type_(get_constructor_type<T>::run(value))
^
../main.cpp:174:21: note: in instantiation of function template specialization 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::Variant<Test &>' requested here
using VariantBase::VariantBase;
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization 'std::_Construct<Test, Test &>' requested here
std::_Construct(std::__addressof(*__cur), *__first);
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:125:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<Test *, Test *>' requested here
__uninit_copy(__first, __last, __result);
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:278:19: note: in instantiation of function template specialization 'std::uninitialized_copy<Test *, Test *>' requested here
{ return std::uninitialized_copy(__first, __last, __result); }
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:299:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<Test *, Test *, Test>' requested here
return std::__uninitialized_copy_a
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:421:15: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<Test *, Test *, std::allocator<Test> >' requested here
= std::__uninitialized_move_if_noexcept_a
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
_M_emplace_back_aux(std::forward<_Args>(__args)...);
^
../main.cpp:181:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::emplace_back<std::unique_ptr<int, std::default_delete<int> > >' requested here
v.emplace_back(std::move(std::unique_ptr<int>(new int)));
^
../main.cpp:53:14: note: previous declaration is here
static int run(const T&)
^
../main.cpp:18:3: error: static_assert failed "There is no such type in this variant"
static_assert(value >= 0, "There is no such type in this variant");
^ ~~~~~~~~~~
../main.cpp:60:11: note: in instantiation of template class 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::type_of<Test &>' requested here
return type_of<T>::value;
^
../main.cpp:31:34: note: in instantiation of member function 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::get_constructor_type<Test &, false>::run' requested here
type_(get_constructor_type<T>::run(value))
^
../main.cpp:174:21: note: in instantiation of function template specialization 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::Variant<Test &>' requested here
using VariantBase::VariantBase;
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization 'std::_Construct<Test, Test &>' requested here
std::_Construct(std::__addressof(*__cur), *__first);
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:125:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<Test *, Test *>' requested here
__uninit_copy(__first, __last, __result);
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:278:19: note: in instantiation of function template specialization 'std::uninitialized_copy<Test *, Test *>' requested here
{ return std::uninitialized_copy(__first, __last, __result); }
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:299:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<Test *, Test *, Test>' requested here
return std::__uninitialized_copy_a
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:421:15: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<Test *, Test *, std::allocator<Test> >' requested here
= std::__uninitialized_move_if_noexcept_a
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
_M_emplace_back_aux(std::forward<_Args>(__args)...);
^
../main.cpp:181:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::emplace_back<std::unique_ptr<int, std::default_delete<int> > >' requested here
v.emplace_back(std::move(std::unique_ptr<int>(new int)));
^
../main.cpp:120:14: error: cannot allocate reference type 'Test &' with new
new (mem) T(std::forward<T>(other));
^
../main.cpp:33:25: note: in instantiation of member function 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::construct_move<Test &, 0, 3, false>::run' requested here
construct_move<T, 0>::run(mem_, std::forward<T>(value));
^
../main.cpp:174:21: note: in instantiation of function template specialization 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::Variant<Test &>' requested here
using VariantBase::VariantBase;
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization 'std::_Construct<Test, Test &>' requested here
std::_Construct(std::__addressof(*__cur), *__first);
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:125:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<Test *, Test *>' requested here
__uninit_copy(__first, __last, __result);
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:278:19: note: in instantiation of function template specialization 'std::uninitialized_copy<Test *, Test *>' requested here
{ return std::uninitialized_copy(__first, __last, __result); }
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:299:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<Test *, Test *, Test>' requested here
return std::__uninitialized_copy_a
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:421:15: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<Test *, Test *, std::allocator<Test> >' requested here
= std::__uninitialized_move_if_noexcept_a
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
_M_emplace_back_aux(std::forward<_Args>(__args)...);
^
../main.cpp:181:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::emplace_back<std::unique_ptr<int, std::default_delete<int> > >' requested here
v.emplace_back(std::move(std::unique_ptr<int>(new int)));
^
Note that somewhere in the middle of the trace unique_ptr disappears and is replaced with Test&. My understanding is that emplace_back works correctly and after the
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
_M_emplace_back_aux(std::forward<_Args>(__args)...);
the item was successfully constructed into the variant as it should be, but then it seems to attempt an "uninitiliazed_copy" of some kind the new item for some reason, calling the copy/move-constructor again, but with a non-const reference this time. Why emplace_back makes a copy at all?
Obviously my fabolus check for "same type" (i.e. detecting a whole-variant copy/move attempt to trigger the more complex find-out-what-type-we-are logic) fails when it receives a reference type. Well, that's logical.
Alright, I've attempted to hack-around this by placing decaying a type within the check
template <typename T>
struct is_self : public std::is_base_of<VariantBase, typename std::decay<T>::type> {};
... to no avail, though
../main.cpp:33:35: error: rvalue reference to type 'VariantBase' (aka 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >') cannot bind to lvalue of type 'Test'
construct_move<T, 0>::run(mem_, std::forward<T>(value));
^~~~~~~~~~~~~~~~~~~~~~
../main.cpp:174:21: note: in instantiation of function template specialization 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::Variant<Test &>' requested here
using VariantBase::VariantBase;
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization 'std::_Construct<Test, Test &>' requested here
std::_Construct(std::__addressof(*__cur), *__first);
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:125:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<Test *, Test *>' requested here
__uninit_copy(__first, __last, __result);
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:278:19: note: in instantiation of function template specialization 'std::uninitialized_copy<Test *, Test *>' requested here
{ return std::uninitialized_copy(__first, __last, __result); }
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:299:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<Test *, Test *, Test>' requested here
return std::__uninitialized_copy_a
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:421:15: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<Test *, Test *, std::allocator<Test> >' requested here
= std::__uninitialized_move_if_noexcept_a
^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
_M_emplace_back_aux(std::forward<_Args>(__args)...);
^
../main.cpp:181:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::emplace_back<std::unique_ptr<int, std::default_delete<int> > >' requested here
v.emplace_back(std::move(std::unique_ptr<int>(new int)));
^
../main.cpp:129:44: note: passing argument to parameter 'other' here
static void run(char* mem, VariantBase&& other)
Well, that's logical too. Of course, I may implement a couple more complex checks for reference types here, but this will only increase the already complex code and, more importantly, I don't clearly understand what I'm gonna do with a lvalue reference - convert it into rvalue and process with move semantic, maybe...
So, my questions are:
Am I doing all this wrong? Is there any easier way, possibly by using more explicit functions? Or maybe I should use a non-qualified template types (template Vector(T something)) and then check for constness (copy vs move), references and types?
What is happening within vector and what is the right way to react to such things?