constexpr template argument weirdness - c++

GCC (5.3) & Clang (3.8) claim that the first line in test is bad, but the second one is fine. MSVC (2015.2) says, both are invalid.
template< typename N, typename T >
void f( N n, T t ) { std::get< n >( t ); }
void test() {
std::get< std::integral_constant< size_t, 0 >() >( std::make_tuple( 123 ) ); // not ok
f( std::integral_constant< size_t, 0 >(), std::make_tuple( 123 ) ); // ok for gcc, clang, but not msvc
}
What, exactly, according to the standard, is the difference? Is this code legal to begin with?
clang error for the first line:
In file included from main.cpp:2:
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/tuple:874:34: error: no matching function for call to '__get_helper2'
{ return std::forward<_Tp&&>(std::__get_helper2<_Tp>(__t)); }
^~~~~~~~~~~~~~~~~~~~~~~
main.cpp:10:10: note: in instantiation of function template specialization 'std::get<std::integral_constant<unsigned long, 0> (), int>' requested here
std::get<std::integral_constant<size_t, 0>()>( std::make_tuple( 123 ) ); // not ok
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/tuple:856:5: note: candidate template ignored: could not match '_Tuple_impl' against 'tuple'
__get_helper2(_Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
^
/usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0/../../../../include/c++/5.3.0/tuple:861:5: note: candidate template ignored: could not match '_Tuple_impl' against 'tuple'
__get_helper2(const _Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
^
1 error generated.
gcc error:
In file included from main.cpp:2:0:
/usr/local/include/c++/5.3.0/tuple: In instantiation of 'constexpr _Tp&& std::get(std::tuple<_Elements ...>&&) [with _Tp = std::integral_constant<long unsigned int, 0ul>(); _Types = {int}]':
main.cpp:10:75: required from here
/usr/local/include/c++/5.3.0/tuple:874:57: error: no matching function for call to '__get_helper2(std::tuple<int>&)'
{ return std::forward<_Tp&&>(std::__get_helper2<_Tp>(__t)); }
^
/usr/local/include/c++/5.3.0/tuple:856:5: note: candidate: template<class _Head, long unsigned int __i, class ... _Tail> constexpr _Head& std::__get_helper2(std::_Tuple_impl<__i, _Head, _Tail ...>&)
__get_helper2(_Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
^
/usr/local/include/c++/5.3.0/tuple:856:5: note: template argument deduction/substitution failed:
/usr/local/include/c++/5.3.0/tuple:874:57: note: mismatched types 'std::integral_constant<long unsigned int, 0ul>()' and 'int'
{ return std::forward<_Tp&&>(std::__get_helper2<_Tp>(__t)); }
^
/usr/local/include/c++/5.3.0/tuple:874:57: note: 'std::tuple<int>' is not derived from 'std::_Tuple_impl<__i, std::integral_constant<long unsigned int, 0ul>(), _Tail ...>'
/usr/local/include/c++/5.3.0/tuple:861:5: note: candidate: template<class _Head, long unsigned int __i, class ... _Tail> constexpr const _Head& std::__get_helper2(const std::_Tuple_impl<__i, _Head, _Tail ...>&)
__get_helper2(const _Tuple_impl<__i, _Head, _Tail...>& __t) noexcept
^
/usr/local/include/c++/5.3.0/tuple:861:5: note: template argument deduction/substitution failed:
/usr/local/include/c++/5.3.0/tuple:874:57: note: mismatched types 'std::integral_constant<long unsigned int, 0ul>()' and 'int'
{ return std::forward<_Tp&&>(std::__get_helper2<_Tp>(__t)); }
^
/usr/local/include/c++/5.3.0/tuple:874:57: note: 'std::tuple<int>' is not derived from 'const std::_Tuple_impl<__i, std::integral_constant<long unsigned int, 0ul>(), _Tail ...>'

tl;dr I think gcc and clang's behavior in both cases is correct.
There is a subtle difference between your two calls. Let me just add an alias to illustrate:
using Zero = std::integral_constant<size_t, 0>;
auto tuple = std::make_tuple(123);
When you write:
std::get<Zero()>(tuple);
Zero() is a type. It is a nullary function returning a Zero. As such, you're calling the version of std::get that takes a type: std::get<T>(). Since there is no element in tuple that has type Zero(), this is an error.
On the other hand, when you write:
Zero n;
std::get<n>(tuple);
n is not a type - it is only ever a value. Since std::integral_constant has a constexpr operator size_t(), that one gets used and you end up calling std::get<I>(), which does what you'd expect.
The same could be accomplished by simply using brace initialization as well:
std::get<Zero{}>(tuple);
since Zero{} is definitely not a type.

Related

Why is compiler giving error while using make_unique with array?

Why can't i initialize a unique pointer
#include <iostream>
#include <memory>
class Widget
{
std::unique_ptr<int[]> arr;
public:
Widget(int size)
{
arr = std::make_unique<int[size]>();
}
~Widget()
{
}
};
int main()
{
}
I am not able to understand the meaning of below error messages. What is wrong with my invocation of
arr = std::make_unique<int[size]>();
I am just trying to create a class with unique pointer member which manages an array. How should I change the below program and WHY?
Error(s):
1129699974/source.cpp: In constructor ‘Widget::Widget(int)’:
1129699974/source.cpp:13:43: error: no matching function for call to ‘make_unique<int [size]>()’
arr = std::make_unique<int[size]>();
^
In file included from /usr/include/c++/7/memory:80:0,
from 1129699974/source.cpp:4:
/usr/include/c++/7/bits/unique_ptr.h:824:5: note: candidate: template<class _Tp, class ... _Args> typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...)
make_unique(_Args&&... __args)
^~~~~~~~~~~
/usr/include/c++/7/bits/unique_ptr.h:824:5: note: template argument deduction/substitution failed:
/usr/include/c++/7/bits/unique_ptr.h: In substitution of ‘template<class _Tp, class ... _Args> typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = int [size]; _Args = {}]’:
1129699974/source.cpp:13:43: required from here
/usr/include/c++/7/bits/unique_ptr.h:824:5: error: ‘int [size]’ is a variably modified type
/usr/include/c++/7/bits/unique_ptr.h:824:5: error: trying to instantiate ‘template<class _Tp> struct std::_MakeUniq’
/usr/include/c++/7/bits/unique_ptr.h:830:5: note: candidate: template<class _Tp> typename std::_MakeUniq<_Tp>::__array std::make_unique(std::size_t)
make_unique(size_t __num)
^~~~~~~~~~~
/usr/include/c++/7/bits/unique_ptr.h:830:5: note: template argument deduction/substitution failed:
/usr/include/c++/7/bits/unique_ptr.h: In substitution of ‘template<class _Tp> typename std::_MakeUniq<_Tp>::__array std::make_unique(std::size_t) [with _Tp = int [size]]’:
1129699974/source.cpp:13:43: required from here
/usr/include/c++/7/bits/unique_ptr.h:830:5: error: ‘int [size]’ is a variably modified type
/usr/include/c++/7/bits/unique_ptr.h:830:5: error: trying to instantiate ‘template<class _Tp> struct std::_MakeUniq’
/usr/include/c++/7/bits/unique_ptr.h:836:5: note: candidate: template<class _Tp, class ... _Args> typename std::_MakeUniq<_Tp>::__invalid_type std::make_unique(_Args&& ...) <deleted>
make_unique(_Args&&...) = delete;
^~~~~~~~~~~
/usr/include/c++/7/bits/unique_ptr.h:836:5: note: template argument deduction/substitution failed:
/usr/include/c++/7/bits/unique_ptr.h: In substitution of ‘template<class _Tp, class ... _Args> typename std::_MakeUniq<_Tp>::__invalid_type std::make_unique(_Args&& ...) [with _Tp = int [size]; _Args = {}]’:
1129699974/source.cpp:13:43: required from here
/usr/include/c++/7/bits/unique_ptr.h:836:5: error: ‘int [size]’ is a variably modified type
/usr/include/c++/7/bits/unique_ptr.h:836:5: error: trying to instantiate ‘template<class _Tp> struct std::_MakeUniq’
You need
arr = std::make_unique<int[]>(size);
make_unique has a specific documented usage:
template< class T > unique_ptr<T> make_unique( std::size_t size );`
... Constructs an array of the given dynamic size. The array elements are value-initialized. This overload participates in overload resolution only if T is an array of unknown bound. The function is equivalent to:
unique_ptr<T>(new std::remove_extent_t<T>[size]())
Conceptually-speaking1, you can think of make_unique as being very similar to std::unique_ptr<T>(new T(...)) where ... represents forwarding of the args passed to make_unique. In this case, the usage is analogous to forwarding the one arg to operator new[] (std::size_t size)
1 The documentation I linked above doesn't say that the arg is forwarded in the array case, and I don't believe the spec does either.

Why type unsigned makes std::variant<int64_t,uint64_t> ambiguous to construct?

I'd like to use simply unsigned(123) and 123u rather than 123ul and (unsigned long)(123).
#include <cstdint>
#include <variant>
using namespace std;
int main()
{
using var_t = variant<int64_t,uint64_t,double>;
var_t v1{1};
var_t v2{1ul};
var_t v3{1u}; //ERROR
// var_t v4{unsigned(1)}; //ERROR
var_t v5{uint64_t(1)};
}
live example
Compiler output
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'int main()':
main.cpp:10:15: error: no matching function for call to 'std::variant<long int, long unsigned int, double>::variant(<brace-enclosed initializer list>)'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1402:2: note: candidate: 'template<long unsigned int _Np, class _Up, class ... _Args, class _Tp, class> constexpr std::variant<_Types>::variant(std::in_place_index_t<_Np>, std::initializer_list<_Up>, _Args&& ...) [with long unsigned int _Np = _Np; _Up = _Up; _Args = {_Args ...}; _Tp = _Tp; <template-parameter-2-5> = <template-parameter-1-5>; _Types = {long int, long unsigned int, double}]'
1402 | variant(in_place_index_t<_Np>, initializer_list<_Up> __il,
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1402:2: note: template argument deduction/substitution failed:
main.cpp:10:15: note: mismatched types 'std::in_place_index_t<_Idx>' and 'unsigned int'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1391:2: note: candidate: 'template<long unsigned int _Np, class ... _Args, class _Tp, class> constexpr std::variant<_Types>::variant(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = _Np; _Args = {_Args ...}; _Tp = _Tp; <template-parameter-2-4> = <template-parameter-1-4>; _Types = {long int, long unsigned int, double}]'
1391 | variant(in_place_index_t<_Np>, _Args&&... __args)
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1391:2: note: template argument deduction/substitution failed:
main.cpp:10:15: note: mismatched types 'std::in_place_index_t<_Idx>' and 'unsigned int'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1381:2: note: candidate: 'template<class _Tp, class _Up, class ... _Args, class> constexpr std::variant<_Types>::variant(std::in_place_type_t<_Tp>, std::initializer_list<_Up>, _Args&& ...) [with _Tp = _Tp; _Up = _Up; _Args = {_Args ...}; <template-parameter-2-4> = <template-parameter-1-4>; _Types = {long int, long unsigned int, double}]'
1381 | variant(in_place_type_t<_Tp>, initializer_list<_Up> __il,
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1381:2: note: template argument deduction/substitution failed:
main.cpp:10:15: note: mismatched types 'std::in_place_type_t<_Tp>' and 'unsigned int'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1371:2: note: candidate: 'template<class _Tp, class ... _Args, class> constexpr std::variant<_Types>::variant(std::in_place_type_t<_Tp>, _Args&& ...) [with _Tp = _Tp; _Args = {_Args ...}; <template-parameter-2-3> = <template-parameter-1-3>; _Types = {long int, long unsigned int, double}]'
1371 | variant(in_place_type_t<_Tp>, _Args&&... __args)
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1371:2: note: template argument deduction/substitution failed:
main.cpp:10:15: note: mismatched types 'std::in_place_type_t<_Tp>' and 'unsigned int'
10 | var_t v3{1u}; //ERROR
| ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1361:2: note: candidate: 'template<class _Tp, class, class, class _Tj, class> constexpr std::variant<_Types>::variant(_Tp&&) [with _Tp = _Tp; <template-parameter-2-2> = <template-parameter-1-2>; <template-parameter-2-3> = <template-parameter-1-3>; _Tj = _Tj; <template-parameter-2-5> = <template-parameter-1-5>; _Types = {long int, long unsigned int, double}]'
1361 | variant(_Tp&& __t)
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1361:2: note: template argument deduction/substitution failed:
/usr/local/include/c++/10.2.0/variant: In substitution of 'template<class ... _Types> template<class _Tp, class> using __accepted_type = std::variant<_Types>::__to_type<__accepted_index<_Tp> > [with _Tp = unsigned int&&; <template-parameter-2-2> = void; _Types = {long int, long unsigned int, double}]':
/usr/local/include/c++/10.2.0/variant:1357:9: required from here
/usr/local/include/c++/10.2.0/variant:1327:8: error: no type named 'type' in 'struct std::enable_if<false, void>'
1327 | using __accepted_type = __to_type<__accepted_index<_Tp>>;
| ^~~~~~~~~~~~~~~
/usr/local/include/c++/10.2.0/variant:1349:7: note: candidate: 'constexpr std::variant<_Types>::variant(std::variant<_Types>&&) [with _Types = {long int, long unsigned int, double}]'
1349 | variant(variant&&) = default;
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1349:15: note: no known conversion for argument 1 from 'unsigned int' to 'std::variant<long int, long unsigned int, double>&&'
1349 | variant(variant&&) = default;
| ^~~~~~~~~
/usr/local/include/c++/10.2.0/variant:1348:7: note: candidate: 'constexpr std::variant<_Types>::variant(const std::variant<_Types>&) [with _Types = {long int, long unsigned int, double}]'
1348 | variant(const variant& __rhs) = default;
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1348:30: note: no known conversion for argument 1 from 'unsigned int' to 'const std::variant<long int, long unsigned int, double>&'
1348 | variant(const variant& __rhs) = default;
| ~~~~~~~~~~~~~~~^~~~~
/usr/local/include/c++/10.2.0/variant:1347:7: note: candidate: 'constexpr std::variant<_Types>::variant() [with _Types = {long int, long unsigned int, double}]'
1347 | variant() = default;
| ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1347:7: note: candidate expects 0 arguments, 1 provided
main.cpp:10:10: warning: unused variable 'v3' [-Wunused-variable]
10 | var_t v3{1u}; //ERROR
| ^~
The final rule we landed on for variant is that we consider only alternatives where the conversion is non-narrowing, and that narrowing determination considers the type, not the value.
Among other things:
integer to floating point is always narrowing.
signed to unsigned is always narrowing.
unsigned to signed is narrowing if the signed type can't represent all possible values of the unsigned type.
So:
For v1, the source type is int, the only non-narrowing alternative is int64_t, and it is chosen.
For v2, the source type is unsigned long. If sizeof(int64_t) == sizeof(unsigned long), as is the case for many platforms, then the only non-narrowing alternative is uint64_t, and that is chosen.
For v3, the source type is unsigned int. If int is 32 bits then both int64_t and uint64_t are viable options, and neither is better than the other. So it is ambiguous.
For this kind of thing, it's a lot clearer to use in_place_type to explicitly pick which alternative you want, instead of asking the reader to do overload resolution in their head.
The answer would be platform dependent, but on my platform (64 bit linux + gcc C++) your results make perfect sense. Just note, in all below results conversion to double has lower rank than integral promotions, so it doesn't participate.
// Below works, as 1 is signed, and can only be promoted to int64_t.
variant<int64_t,uint64_t,double> v1{1};
// Below works, as 1ul is exactly unsigned long (64 bit for me), and is exact match
variant<int64_t,uint64_t,double> v2{1ul};
// Below doesn't work, as 1u is unsigned int, and can be equally promoted to int64_t and uint64_t - thus ambiguity
variant<int64_t,uint64_t,double> v3{1u}; //ERROR
// Below doesn't work, for exactly the same reason as one above.
variant<int64_t,uint64_t,double> v4{unsigned(1)}; //ERROR
}
I found an elegant solution with extending std::variant:
#include <cstdint>
#include <variant>
using namespace std;
struct var_t : variant<int64_t,uint64_t,double> {
using variant<int64_t,uint64_t,double>::variant;
constexpr var_t(unsigned u) : variant(uint64_t(u)) {}
};
int main()
{
var_t v1{1};
var_t v2{1ul};
var_t v3{1u}; // VALID
var_t v4{unsigned(1)}; // VALID
var_t v5{uint64_t(1)};
}
live example
No need to use cumbersome in_place_type. Sure it is useful but not this case.
Thanks everybody for attention

C++17 variant<any> inside the class

The following code compiles well:
int main()
{
variant<any> var;
var = 5;
cout << any_cast<int>(get<any>(var)) << endl;
return 0;
}
But when I'm trying to put variant<any> as a class member
struct MyClass{
variant<any> var;
};
int main()
{
MyClass s;
return 0;
}
It doesn't compile. Am I doing something wrong or it is some bug?
I'm using gcc 7.1.0
In file included from /home/zak/Projects/Anytest/main.cpp:3:0:
/usr/local/gcc-7.1/include/c++/7.1.0/variant: In instantiation of ‘struct std::__detail::__variant::__accepted_index<const std::variant<std::any>&, std::variant<std::any>, void>’:
/usr/local/gcc-7.1/include/c++/7.1.0/variant:911:26: required from ‘constexpr const size_t std::variant<std::any>::__accepted_index<const std::variant<std::any>&>’
/usr/local/gcc-7.1/include/c++/7.1.0/variant:940:6: required by substitution of ‘template<class _Tp, class> constexpr std::variant<std::any>::variant(_Tp&&) [with _Tp = const std::variant<std::any>&; <template-parameter-1-2> = <missing>]’
/home/zak/Projects/Anytest/main.cpp:14:13: required from here
/usr/local/gcc-7.1/include/c++/7.1.0/variant:559:49: error: no matching function for call to ‘std::__detail::__variant::__overload_set<std::any>::_S_fun(const std::variant<std::any>&)’
decltype(__overload_set<_Types...>::_S_fun(std::declval<_Tp>()),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:541:58: note: candidate: static std::integral_constant<long unsigned int, sizeof... (_Rest)> std::__detail::__variant::__overload_set<_First, _Rest ...>::_S_fun(_First) [with _First = std::any; _Rest = {}]
static integral_constant<size_t, sizeof...(_Rest)> _S_fun(_First);
^~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:541:58: note: no known conversion for argument 1 from ‘const std::variant<std::any>’ to ‘std::any’
/usr/local/gcc-7.1/include/c++/7.1.0/variant:535:19: note: candidate: static void std::__detail::__variant::__overload_set<_Types>::_S_fun() [with _Types = {}]
{ static void _S_fun(); };
^~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:535:19: note: candidate expects 0 arguments, 1 provided
/usr/local/gcc-7.1/include/c++/7.1.0/variant:559:49: error: no matching function for call to ‘std::__detail::__variant::__overload_set<std::any>::_S_fun(const std::variant<std::any>&)’
decltype(__overload_set<_Types...>::_S_fun(std::declval<_Tp>()),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:541:58: note: candidate: static std::integral_constant<long unsigned int, sizeof... (_Rest)> std::__detail::__variant::__overload_set<_First, _Rest ...>::_S_fun(_First) [with _First = std::any; _Rest = {}]
static integral_constant<size_t, sizeof...(_Rest)> _S_fun(_First);
^~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:541:58: note: no known conversion for argument 1 from ‘const std::variant<std::any>’ to ‘std::any’
/usr/local/gcc-7.1/include/c++/7.1.0/variant:535:19: note: candidate: static void std::__detail::__variant::__overload_set<_Types>::_S_fun() [with _Types = {}]
{ static void _S_fun(); };
^~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:535:19: note: candidate expects 0 arguments, 1 provided
/usr/local/gcc-7.1/include/c++/7.1.0/variant:559:49: error: no matching function for call to ‘std::__detail::__variant::__overload_set<std::any>::_S_fun(const std::variant<std::any>&)’
decltype(__overload_set<_Types...>::_S_fun(std::declval<_Tp>()),
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:541:58: note: candidate: static std::integral_constant<long unsigned int, sizeof... (_Rest)> std::__detail::__variant::__overload_set<_First, _Rest ...>::_S_fun(_First) [with _First = std::any; _Rest = {}]
static integral_constant<size_t, sizeof...(_Rest)> _S_fun(_First);
^~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:541:58: note: no known conversion for argument 1 from ‘const std::variant<std::any>’ to ‘std::any’
/usr/local/gcc-7.1/include/c++/7.1.0/variant:535:19: note: candidate: static void std::__detail::__variant::__overload_set<_Types>::_S_fun() [with _Types = {}]
{ static void _S_fun(); };
^~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:535:19: note: candidate expects 0 arguments, 1 provided
/usr/local/gcc-7.1/include/c++/7.1.0/variant: In instantiation of ‘constexpr const size_t std::__detail::__variant::__accepted_index<const std::variant<std::any>&, std::variant<std::any>, void>::value’:
/usr/local/gcc-7.1/include/c++/7.1.0/variant:911:26: required from ‘constexpr const size_t std::variant<std::any>::__accepted_index<const std::variant<std::any>&>’
/usr/local/gcc-7.1/include/c++/7.1.0/variant:940:6: required by substitution of ‘template<class _Tp, class> constexpr std::variant<std::any>::variant(_Tp&&) [with _Tp = const std::variant<std::any>&; <template-parameter-1-2> = <missing>]’
/home/zak/Projects/Anytest/main.cpp:14:13: required from here
/usr/local/gcc-7.1/include/c++/7.1.0/variant:564:12: error: no matching function for call to ‘std::__detail::__variant::__overload_set<std::any>::_S_fun(const std::variant<std::any>&)’
- decltype(__overload_set<_Types...>::
~~~~~~~~~~~~~~~~~~~~~~~~~~~
_S_fun(std::declval<_Tp>()))::value;
~~~~~~^~~~~~~~~~~~~~~~~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:541:58: note: candidate: static std::integral_constant<long unsigned int, sizeof... (_Rest)> std::__detail::__variant::__overload_set<_First, _Rest ...>::_S_fun(_First) [with _First = std::any; _Rest = {}]
static integral_constant<size_t, sizeof...(_Rest)> _S_fun(_First);
^~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:541:58: note: no known conversion for argument 1 from ‘const std::variant<std::any>’ to ‘std::any’
/usr/local/gcc-7.1/include/c++/7.1.0/variant:535:19: note: candidate: static void std::__detail::__variant::__overload_set<_Types>::_S_fun() [with _Types = {}]
{ static void _S_fun(); };
^~~~~~
/usr/local/gcc-7.1/include/c++/7.1.0/variant:535:19: note: candidate expects 0 arguments, 1 provided
The problem is with MyClass's implicitly defined copy constructor, when it tries to copy construct the member of type std::variant<std::any>.
To perform overload resolution, the compiler first needs to try to instantiate all of std::variant's constructor templates, with the function argument being a const std::variant<std::any>&1. The constructor of our interest is this one:
template <class T> constexpr variant(T&& t) noexcept(/*...*/);
It only participates in overload resolution if, among others, the expression FUN(​std​::​forward<T>(t)) is well formed, where FUN is a set of overloaded functions produced according to [variant.ctor]/12.2
In this case, there is only one alternative type (std::any), so there is only one imaginary function FUN, whose signature is FUN(std::any). Now, the compiler needs to decide whether FUN can be called with a const std::variant<std::any>&1. In this process, the compiler needs to know whether std::any can be constructed with const std::variant<std::any>&1.
This will trigger the instantiation of std::any's constructor template template<class T> any(T&& value);, which only participates in overload resolution if std::is_­copy_­constructible_­v<VT> is true (VT being std::decay_t<T>, and T being const std::variant<std::any>&).
Now in order to see whether VT (i.e. std::variant<std::any>) is copy constructible, the compiler needs to try to instantiate all of std::variant's constructor templates... and this is where we started, and we are stuck in a loop.
This can explain why we see template<class _Tp, class> constexpr std::variant<std::any>::variant(_Tp&&) and __overload_set<std::any>::_S_fun (which corresponds to the function FUN mentioned above) in the error message, and why we see the same error appearing several times.
It remains a question how GCC breaks from the above loop, and why tweaking the program can stop GCC from reporting the error. Maybe these are indication of some bug.
1. Strictly speaking, it should be "an lvalue of type const std::variant<std::any>" rather than "a const std::variant<std::any>&".
2. The standard also requires that this constructor shall only participate in overload resolution if is_­same_­v<decay_­t<T>, variant> is false. GCC (libstdc++) chooses to check that later. I don't know whether this is conforming.

Where is piecewise_construct for boost::container::map::emplace()?

Using Boost, I am trying to emplace() a key/value pair into a boost::container::map. The value needs multiple arguments for the constructor. From what I've been able to find, I need to use piecewise_construct and pass the constructor args in tuples. This works with std::map<K,V>, but I can't get it to work using boost::container::map<K,V> The closest Boost doc I can find shows boost::unordered_multimap<K,V>, but not a plain map<K,V>.
#include <map>
#include <boost/tuple/tuple.hpp>
#include <boost/container/map.hpp>
#include <boost/unordered_set.hpp>
class A {
public:
/**/ A( int ) { }
bool operator<( const A & ) const { return false; }
} ;
class B {
public:
/**/ B( int, const char * ) { }
} ;
int
main( int, char ** )
{
A a( 100 );
B b( 200, "foo" );
std::map<A,B> mgood;
mgood.emplace( std::piecewise_construct,
std::make_tuple( 100 ),
std::make_tuple( 200, "Hello" ) );
#if 1
boost::container::map<A,B> mbad;
mbad.emplace( boost::unordered::piecewise_construct,
boost::make_tuple( 300 ),
boost::make_tuple( 400, "World" ) );
#endif
}
The g++-4.9.2 error messages are impenetrable (to me, anyway):
make -k tst g++ -DBOOST_LOG_DYN_LINK -g -std=c++11 -c -o tst.o
tst.cc In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp: In
instantiation of âstatic void
boost::container::allocator_traits::priv_construct(boost::move_detail::false_type,
Allocator&, T*, Args&& ...) [with T =
boost::container::container_detail::pair; Args = {const
boost::unordered::piecewise_construct_t&, boost::tuples::tuple, boost::tuples::tuple}; Allocator =
boost::container::new_allocator, void*, (boost::container::tree_type_enum)0u, true> >;
boost::move_detail::false_type =
boost::move_detail::integral_constant]â:
/usr/local/include/boost/container/allocator_traits.hpp:353:86:
required from âstatic void
boost::container::allocator_traits::construct(Allocator&,
T*, Args&& ...) [with T = boost::container::container_detail::pair; Args = {const boost::unordered::piecewise_construct_t&,
boost::tuples::tuple,
boost::tuples::tuple}; Allocator =
boost::container::new_allocator, void*, (boost::container::tree_type_enum)0u, true> >]â
/usr/local/include/boost/container/detail/node_alloc_holder.hpp:167:81:
required from
âboost::container::container_detail::node_alloc_holder::NodePtr
boost::container::container_detail::node_alloc_holder::create_node(Args&& ...) [with Args = {const
boost::unordered::piecewise_construct_t&, boost::tuples::tuple, boost::tuples::tuple}; Allocator =
boost::container::new_allocator >; ICont =
boost::intrusive::rbtree_impl, void*, (boost::container::tree_type_enum)0u, true>,
boost::intrusive::rbtree_node_traits,
(boost::intrusive::link_mode_type)0u, boost::intrusive::dft_tag, 3u>,
void,
boost::container::value_to_node_compare, void*, (boost::container::tree_type_enum)0u, true>,
boost::intrusive::tree_value_compare,
std::less,
boost::container::container_detail::select1st >
, long unsigned int, true, void>; boost::container::container_detail::node_alloc_holder::NodePtr =
boost::container::container_detail::tree_node,
void*, (boost::container::tree_type_enum)0u, true>]â
/usr/local/include/boost/container/detail/tree.hpp:922:94: required
from
âstd::pair,
Options:: tree_type, Options:: optimize_size>::type::iterator, false>,
bool> boost::container::container_detail::tree::emplace_unique(Args&& ...) [with Args =
{const boost::unordered::piecewise_construct_t&,
boost::tuples::tuple,
boost::tuples::tuple}; Key = A; T = std::pair;
KeyOfValue =
boost::container::container_detail::select1st >;
Compare = std::less; Allocator =
boost::container::new_allocator >; Options =
boost::container::tree_opt<(boost::container::tree_type_enum)0u,
true>; typename
boost::container::container_detail::intrusive_tree_type,
Options:: tree_type, Options:: optimize_size>::type::iterator =
boost::intrusive::tree_iterator, void*, (boost::container::tree_type_enum)0u, true>,
boost::intrusive::rbtree_node_traits,
(boost::intrusive::link_mode_type)0u, boost::intrusive::dft_tag, 3u>,
false>]â /usr/local/include/boost/container/map.hpp:665:72:
required from âstd::pair, boost::container::container_detail::select1st >, Compare, Allocator, MapOptions>::iterator, bool> boost::container::map::emplace(Args&& ...) [with Args = {const
boost::unordered::piecewise_construct_t&, boost::tuples::tuple, boost::tuples::tuple}; Key = A; T = B;
Compare = std::less; Allocator =
boost::container::new_allocator >; MapOptions =
boost::container::tree_opt<(boost::container::tree_type_enum)0u,
true>; typename boost::container::container_detail::tree,
boost::container::container_detail::select1st >, Compare, Allocator, MapOptions>::iterator = boost::container::container_detail::iterator_from_iiterator, void*, (boost::container::tree_type_enum)0u, true>,
boost::intrusive::rbtree_node_traits,
(boost::intrusive::link_mode_type)0u, boost::intrusive::dft_tag, 3u>,
false>, false>]â tst.cc:90:53: required from here
/usr/local/include/boost/container/allocator_traits.hpp:408:10: error:
no matching function for call to
âboost::container::container_detail::pair::pair(const
boost::unordered::piecewise_construct_t&, boost::tuples::tuple, boost::tuples::tuple)â
{ ::new((void)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
candidates are: In file included from
/usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:146:4: note:
template
boost::container::container_detail::pair::pair(std::pair<_U1,
_U2>&&)
pair(BOOST_RV_REF_BEG std::pair BOOST_RV_REF_END p)
^ /usr/local/include/boost/container/detail/pair.hpp:146:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
types âstd::pair<_T1, _T2>â and âconst
boost::unordered::piecewise_construct_tâ have incompatible
cv-qualifiers
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:141:4: note:
boost::container::container_detail::pair::pair(std::pair<_T1,
_T2>&&) [with T1 = A; T2 = B]
pair(BOOST_RV_REF_BEG std::pair BOOST_RV_REF_END p)
^ /usr/local/include/boost/container/detail/pair.hpp:141:4: note: candidate expects 1 argument, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:137:4: note:
template
boost::container::container_detail::pair::pair(const
std::pair<_U1, _U2>&)
pair(const std::pair& p)
^ /usr/local/include/boost/container/detail/pair.hpp:137:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
âconst boost::unordered::piecewise_construct_tâ is not derived
from âconst std::pair<_T1, _T2>â
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:132:4: note:
boost::container::container_detail::pair::pair(const
std::pair<_T1, _T2>&) [with T1 = A; T2 = B]
pair(const std::pair& x)
^ /usr/local/include/boost/container/detail/pair.hpp:132:4: note: candidate expects 1 argument, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:126:4: note:
template
boost::container::container_detail::pair::pair(U&&, V&&)
pair(BOOST_FWD_REF(U) u, BOOST_FWD_REF(V) v)
^ /usr/local/include/boost/container/detail/pair.hpp:126:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
candidate expects 2 arguments, 3 provided
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:120:4: note:
boost::container::container_detail::pair::pair(const T1&,
const T2&) [with T1 = A; T2 = B]
pair(const T1 &t1, const T2 &t2)
^ /usr/local/include/boost/container/detail/pair.hpp:120:4: note: candidate expects 2 arguments, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:115:4: note:
template
boost::container::container_detail::pair::pair(boost::container::container_detail::pair&&)
pair(BOOST_RV_REF_BEG pair BOOST_RV_REF_END p)
^ /usr/local/include/boost/container/detail/pair.hpp:115:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
types âboost::container::container_detail::pairâ and
âconst boost::unordered::piecewise_construct_tâ have incompatible
cv-qualifiers
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:110:4: note:
template
boost::container::container_detail::pair::pair(const
boost::container::container_detail::pair&)
pair(const pair &p)
^ /usr/local/include/boost/container/detail/pair.hpp:110:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
âconst boost::unordered::piecewise_construct_tâ is not derived
from âconst boost::container::container_detail::pairâ
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:105:4: note:
boost::container::container_detail::pair::pair(boost::container::container_detail::pair&&) [with T1
= A; T2 = B]
pair(BOOST_RV_REF(pair) p)
^ /usr/local/include/boost/container/detail/pair.hpp:105:4: note: candidate expects 1 argument, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:100:4: note:
boost::container::container_detail::pair::pair(const
boost::container::container_detail::pair&) [with T1 = A; T2 =
B]
pair(const pair& x)
^ /usr/local/include/boost/container/detail/pair.hpp:100:4: note: candidate expects 1 argument, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:95:4: note:
boost::container::container_detail::pair::pair() [with T1 = A;
T2 = B]
pair()
^ /usr/local/include/boost/container/detail/pair.hpp:95:4: note: candidate expects 0 arguments, 3 provided : recipe for target
'tst.o' failed make: *** [tst.o] Error 1 make: Target 'tst' not remade
because of errors.
Compilation exited abnormally with code 2 at Sat Apr 2 17:11:28
Can you point me in a useful direction? (I'd prefer not to mix boost and std containers; it should be possible to emplace into a boost::container::map, right?)
It appears that piecewise_construct is not implemented for boost::pair (which is the type for boost::container::map entries). See .../boost/container/detail/pair.hpp:151:
//piecewise_construct missing
//template <class U, class V> pair(pair<U, V>&& p);
//template <class... Args1, class... Args2>
// pair(piecewise_construct_t, tuple<Args1...> first_args,
// tuple<Args2...> second_args);
I guess the implementation for this is tough.
piecewise_construct support for both C++03 and C++11 capable compilers was added in commit:
https://github.com/boostorg/container/commit/79a75f470e75f35f5f2a91e10fcc67d03b0a2160
and will be officially released in Boost 1.62. The following code compiles fine:
#include <boost/tuple/tuple.hpp>
#include <tuple>
#include <boost/container/map.hpp>
class A {
public:
/**/ A( int ) { }
bool operator<( const A & ) const { return false; }
} ;
class B {
public:
/**/ B( int, const char * ) { }
} ;
int main( int, char *[] )
{
A a( 100 );
B b( 200, "foo" );
boost::container::map<A,B> m;
//1) Both Boost.Tuple and std tuple supported
//2) std:: or boost::container::piecewise_construct supported
m.emplace( boost::container::piecewise_construct,
boost::make_tuple( 300 ),
boost::make_tuple( 400, "World" ) );
m.emplace( std::piecewise_construct,
std::make_tuple( 400 ),
std::make_tuple( 500, "World2" ) );
}

gcc 4.7 STL library deficit on pair implementation?

The following code compiles on gcc 4.6 but not 4.7. Is it 4.7's problem or 4.6's problem? Compiled with -std=gnu++0x.
#include <utility>
using namespace std;
struct Z {
};
struct X {
operator Z*() const { return nullptr; }
};
struct Y {
Y(Z*) {}
};
int main() {
pair<int, Y> p(make_pair(0, X()));
}
Error messages:
[hidden]$ g++-mp-4.6 -std=gnu++0x e.cpp
[hidden]$ g++-mp-4.7 -std=gnu++0x e.cpp
e.cpp: In function 'int main()':
e.cpp:17:37: error: no matching function for call to 'std::pair<int, Y>::pair(std::pair<int, X>)'
e.cpp:17:37: note: candidates are:
In file included from /opt/local/include/gcc47/c++/utility:72:0,
from e.cpp:1:
/opt/local/include/gcc47/c++/bits/stl_pair.h:204:9: note: template<class ... _Args1, long unsigned int ..._Indexes1, class ... _Args2, long unsigned int ..._Indexes2> std::pair::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>)
/opt/local/include/gcc47/c++/bits/stl_pair.h:204:9: note: template argument deduction/substitution failed:
e.cpp:17:37: note: 'std::pair<int, X>' is not derived from 'std::tuple<_Args1 ...>'
In file included from /opt/local/include/gcc47/c++/utility:72:0,
from e.cpp:1:
/opt/local/include/gcc47/c++/bits/stl_pair.h:153:9: note: template<class ... _Args1, class ... _Args2> std::pair::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>)
/opt/local/include/gcc47/c++/bits/stl_pair.h:153:9: note: template argument deduction/substitution failed:
e.cpp:17:37: note: cannot convert 'std::make_pair(_T1&&, _T2&&) [with _T1 = int; _T2 = X; typename std::__decay_and_strip<_T2>::__type = X; typename std::__decay_and_strip<_T1>::__type = int]((* & X()))' (type 'std::pair<int, X>') to type 'std::piecewise_construct_t'
In file included from /opt/local/include/gcc47/c++/utility:72:0,
from e.cpp:1:
/opt/local/include/gcc47/c++/bits/stl_pair.h:148:12: note: template<class _U1, class _U2, class> constexpr std::pair::pair(std::pair<_U1, _U2>&&)
/opt/local/include/gcc47/c++/bits/stl_pair.h:148:12: note: template argument deduction/substitution failed:
/opt/local/include/gcc47/c++/bits/stl_pair.h:145:38: error: no type named 'type' in 'struct std::enable_if<false, void>'
/opt/local/include/gcc47/c++/bits/stl_pair.h:142:12: note: template<class _U1, class _U2, class> constexpr std::pair::pair(_U1&&, _U2&&)
/opt/local/include/gcc47/c++/bits/stl_pair.h:142:12: note: template argument deduction/substitution failed:
e.cpp:17:37: note: candidate expects 2 arguments, 1 provided
In file included from /opt/local/include/gcc47/c++/utility:72:0,
from e.cpp:1:
/opt/local/include/gcc47/c++/bits/stl_pair.h:136:12: note: template<class _U2, class> constexpr std::pair::pair(const _T1&, _U2&&)
/opt/local/include/gcc47/c++/bits/stl_pair.h:136:12: note: template argument deduction/substitution failed:
e.cpp:17:37: note: cannot convert 'std::make_pair(_T1&&, _T2&&) [with _T1 = int; _T2 = X; typename std::__decay_and_strip<_T2>::__type = X; typename std::__decay_and_strip<_T1>::__type = int]((* & X()))' (type 'std::pair<int, X>') to type 'const int&'
In file included from /opt/local/include/gcc47/c++/utility:72:0,
from e.cpp:1:
/opt/local/include/gcc47/c++/bits/stl_pair.h:131:12: note: template<class _U1, class> constexpr std::pair::pair(_U1&&, const _T2&)
/opt/local/include/gcc47/c++/bits/stl_pair.h:131:12: note: template argument deduction/substitution failed:
e.cpp:17:37: note: candidate expects 2 arguments, 1 provided
In file included from /opt/local/include/gcc47/c++/utility:72:0,
from e.cpp:1:
/opt/local/include/gcc47/c++/bits/stl_pair.h:122:7: note: std::pair<_T1, _T2>::pair(std::pair<_T1, _T2>&&) [with _T1 = int; _T2 = Y; std::pair<_T1, _T2> = std::pair<int, Y>]
/opt/local/include/gcc47/c++/bits/stl_pair.h:122:7: note: no known conversion for argument 1 from 'std::pair<int, X>' to 'std::pair<int, Y>&&'
/opt/local/include/gcc47/c++/bits/stl_pair.h:119:17: note: constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = int; _T2 = Y; std::pair<_T1, _T2> = std::pair<int, Y>]
/opt/local/include/gcc47/c++/bits/stl_pair.h:119:17: note: no known conversion for argument 1 from 'std::pair<int, X>' to 'const std::pair<int, Y>&'
/opt/local/include/gcc47/c++/bits/stl_pair.h:116:12: note: template<class _U1, class _U2, class> constexpr std::pair::pair(const std::pair<_U1, _U2>&)
/opt/local/include/gcc47/c++/bits/stl_pair.h:116:12: note: template argument deduction/substitution failed:
/opt/local/include/gcc47/c++/bits/stl_pair.h:113:38: error: no type named 'type' in 'struct std::enable_if<false, void>'
/opt/local/include/gcc47/c++/bits/stl_pair.h:104:26: note: constexpr std::pair<_T1, _T2>::pair(const _T1&, const _T2&) [with _T1 = int; _T2 = Y]
/opt/local/include/gcc47/c++/bits/stl_pair.h:104:26: note: candidate expects 2 arguments, 1 provided
/opt/local/include/gcc47/c++/bits/stl_pair.h:100:26: note: constexpr std::pair<_T1, _T2>::pair() [with _T1 = int; _T2 = Y]
/opt/local/include/gcc47/c++/bits/stl_pair.h:100:26: note: candidate expects 0 arguments, 1 provided
That shouldn't compile.
The initialisation of p.second requires an implicit conversion from X to Y. An implicit conversion can only involve at most one user-defined conversion The required conversion would require two; X to Z* via the conversion operator, and Z* to Y via the conversion constructor.
Initialisation of pair elements from another pair is only allowed via implicit conversions. C++11 says:
20.3.2/12 This constructor shall not participate in overload resolution unless const U& is implicitly convertible to first_type and const V& is implicitly convertible to second_type.
and C++98 said:
20.2.2/4 Initializes members from the corresponding members of the argument, performing implicit conversions as needed.
Presumably, the older version had a bug which allowed this conversion to be considered, and that bug has been fixed in the more recent version.