Consider the following erroneous code:
#include <memory> #include <string> struct WidgetOptions { // imagine there is interesting stuff here }; struct Widget { Widget(WidgetOptions const* options); // imagine there is other interesting stuff here }; void oops() { WidgetOptions options; // The next line fails to compile std::shared_ptr<Widget> widget = std::make_shared<Widget>(options); }
Here comes the error explosion.
// gcc In file included from bits/stl_tempbuf.h:61, from memory:66, from sample.cpp:1: bits/stl_construct.h: In instantiation of 'void std::_Construct(_Tp*, _Args&& ...) [with _Tp = Widget; _Args = {WidgetOptions&}]': bits/alloc_traits.h:657:19: required from 'static void std::allocator_traits<std::allocator<void> >::construct(allocator_type&, _Up*, _Args&& ...) [with _Up = Widget; _Args = {WidgetOptions&}; allocator_type = std::allocator<void>]' 657 | { std::_Construct(__p, std::forward<_Args>(__args)...); } | ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bits/shared_ptr_base.h:607:39: required from 'std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc, _Args&& ...) [with _Args = {WidgetOptions&}; _Tp = Widget; _Alloc = std::allocator<void>; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' 607 | allocator_traits<_Alloc>::construct(__a, _M_ptr(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~ 608 | std::forward<_Args>(__args)...); // might throw | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bits/shared_ptr_base.h:969:16: required from 'std::__shared_count<_Lp>::__shared_count(_Tp*&, std::_Sp_alloc_shared_tag<_Alloc>, _Args&& ...) [with _Tp = Widget; _Alloc = std::allocator<void>; _Args = {WidgetOptions&}; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' 969 | auto __pi = ::new (__mem) | ^~~~~~~~~~~~~ 970 | _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bits/shared_ptr_base.h:1713:14: required from 'std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<void>; _Args = {WidgetOptions&}; _Tp = Widget; __gnu_cxx::_Lock_policy _Lp = __gnu_cxx::_S_atomic]' 1713 | : _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bits/shared_ptr.h:463:59: required from 'std::shared_ptr<_Tp>::shared_ptr(std::_Sp_alloc_shared_tag<_Tp>, _Args&& ...) [with _Alloc = std::allocator<void>; _Args = {WidgetOptions&}; _Tp = Widget]' 463 | : __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...) | ^ bits/shared_ptr.h:1007:14: required from 'std::shared_ptr<typename std::enable_if<(! std::is_array<_Tp>::value), _Tp>::type> std::make_shared(_Args&& ...) [with _Tp = Widget; _Args = {WidgetOptions&}; typename enable_if<(! is_array<_Tp>::value), _Tp>::type = Widget]' 1007 | return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a}, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1008 | std::forward<_Args>(__args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sample.cpp:22:33: required from here 22 | std::make_shared<Widget>(options); | ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ bits/stl_construct.h:119:7: error: no matching function for call to 'Widget::Widget(WidgetOptions&)' 119 | ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sample.cpp:11:5: note: candidate: 'Widget::Widget(const WidgetOptions*)' 11 | Widget(WidgetOptions const* options); | ^~~~~~ sample.cpp:11:33: note: no known conversion for argument 1 from 'WidgetOptions' to 'const WidgetOptions*' 11 | Widget(WidgetOptions const* options); | ~~~~~~~~~~~~~~~~~~~~~^~~~~~~ sample.cpp:9:8: note: candidate: 'constexpr Widget::Widget(const Widget&)' 9 | struct Widget | ^~~~~~ sample.cpp:9:8: note: no known conversion for argument 1 from 'WidgetOptions' to 'const Widget&' sample.cpp:9:8: note: candidate: 'constexpr Widget::Widget(Widget&&)' sample.cpp:9:8: note: no known conversion for argument 1 from 'WidgetOptions' to 'Widget&&' Compiler returned: 1
And for clang:
In file included from sample.cpp:1: In file included from memory:66: In file included from bits/stl_tempbuf.h:61: bits/stl_construct.h:119:25: error: no matching constructor for initialization of 'Widget' 119 | ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ bits/alloc_traits.h:657:9: note: in instantiation of function template specialization 'std::_Construct<Widget, WidgetOptions &>' requested here 657 | { std::_Construct(__p, std::forward<_Args>(__args)...); } | ^ bits/shared_ptr_base.h:607:30: note: in instantiation of function template specialization 'std::allocator_traits<std::allocator<void>>::construct<Widget, WidgetOptions &>' requested here 607 | allocator_traits<_Alloc>::construct(__a, _M_ptr(), | ^ bits/shared_ptr_base.h:970:6: note: in instantiation of function template specialization 'std::_Sp_counted_ptr_inplace<Widget, std::allocator<void>, __gnu_cxx::_S_atomic>::_Sp_counted_ptr_inplace<WidgetOptions &>' requested here 970 | _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...); | ^ bits/shared_ptr_base.h:1713:14: note: in instantiation of function template specialization 'std::__shared_count<>::__shared_count<Widget, std::allocator<void>, WidgetOptions &>' requested here 1713 | : _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...) | ^ bits/shared_ptr.h:463:4: note: in instantiation of function template specialization 'std::__shared_ptr<Widget>::__shared_ptr<std::allocator<void>, WidgetOptions &>' requested here 463 | : __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...) | ^ bits/shared_ptr.h:1007:14: note: in instantiation of function template specialization 'std::shared_ptr<Widget>::shared_ptr<std::allocator<void>, WidgetOptions &>' requested here 1007 | return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a}, | ^ sample.cpp:22:14: note: in instantiation of function template specialization 'std::make_shared<Widget, WidgetOptions &>' requested here 22 | std::make_shared<Widget>(options); | ^ sample.cpp:11:5: note: candidate constructor not viable: no known conversion from 'WidgetOptions' to 'const WidgetOptions *' for 1st argument; take the address of the argument with & 11 | Widget(WidgetOptions const* options); | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ sample.cpp:9:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'WidgetOptions' to 'const Widget' for 1st argument 9 | struct Widget | ^~~~~~ sample.cpp:9:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'WidgetOptions' to 'Widget' for 1st argument 9 | struct Widget | ^~~~~~ 1 error generated. Compiler returned: 1
And for msvc:
xutility(408): error C2665: 'Widget::Widget': no overloaded function could convert all the argument types sample.cpp(14): note: could be 'Widget::Widget(Widget &&)' xutility(408): note: 'Widget::Widget(Widget &&)': cannot convert argument 1 from 'WidgetOptions' to 'Widget &&' xutility(408): note: Reason: cannot convert from 'WidgetOptions' to 'Widget' xutility(408): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called sample.cpp(14): note: or 'Widget::Widget(const Widget &)' xutility(408): note: 'Widget::Widget(const Widget &)': cannot convert argument 1 from 'WidgetOptions' to 'const Widget &' xutility(408): note: Reason: cannot convert from 'WidgetOptions' to 'const Widget' xutility(408): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called sample.cpp(11): note: or 'Widget::Widget(const WidgetOptions *)' xutility(408): note: 'Widget::Widget(const WidgetOptions *)': cannot convert argument 1 from 'WidgetOptions' to 'const WidgetOptions *' xutility(408): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called xutility(408): note: while trying to match the argument list '(WidgetOptions)' xutility(408): note: the template instantiation context (the oldest one first) is sample.cpp(22): note: see reference to function template instantiation 'std::shared_ptr<Widget> std::make_shared<Widget,WidgetOptions&>(WidgetOptions &)' being compiled memory(2896): note: see reference to function template instantiation 'std::_Ref_count_obj2<_Ty>::_Ref_count_obj2<WidgetOptions&>(WidgetOptions &)' being compiled with [ _Ty=Widget ] memory(2083): note: see reference to function template instantiation 'void std::_Construct_in_place<_Ty,WidgetOptions&>(_Ty &,WidgetOptions &) noexcept(false)' being compiled with [ _Ty=Widget ] Compiler returned: 2
The trick to understanding C++ compiler error messages is to focus on two things. First, look at the beginning of the error message, which tells you what went wrong at a very low level. Then skip over the intermediate errors that follow the chain of calls until you end up at the line of code that you wrote. That original line of code is the one that is leading the compiler to a bad place. After that, you sometimes get supplemental information that helps you understand the low-level error better.
For the gcc error, it starts with trying to call void std::_Construct(_Tp*, _Args&& ...)
, and if you substitute the template parameters, it expands to void std::_Construct(Widget*, WidgetOptions&)
. From the function name, it sounds like it’s trying to construct something, and my guess is that it’s trying to construct the type _Tp
(which is Widget
) from the arguments (which are WidgetOptions&
). Then follows a chain of “required from”s that ultimately lead to std::make_shared<Widget>(options)
, which is the line of code that we wrote that created the problem in the first place.
After that comes a little more explanation: “No matching function for call to Widget::
.” So it’s trying to call the Widget
constructor with a WidgetÂOptions&
but can’t, and the rest of the error message is listing the various constructor candidates and why each one doesn’t work.
For the clang error, it starts with the “No matching constructor” portion, though for some reason it doesn’t tell you what _Tp
and _Args
are. Then comes a chain of “in instantiation of” messages until it reaches std::make_
, which is the line of code we wrote.
After the chain of instantiations, clang provides details on why it couldn’t find a matching constructor.
Finally, we have msvc, which does things in a different order. It starts by telling you that it couldn’t construct a Widget
from the provided arguments. Unfortunately, it doesn’t come right out and tell you what the arguments are, though from the third part of the message “cannot convert argument 1 from ‘WidgetÂOptions
‘ to ‘Widget&&
‘” you can infer that the first argument is a WidgetÂOptions
. After explaining why each constructor candidate was unsuitable, it gives you the function instantiation chain, starting with the std::make_
.
So all of them are telling you that the std::make_
is the line of code that created the problem, and the reason is that it couldn’t find a suitable Widget
constructor.
Study the part of the message that explains why each candidate was unsuitable, and look at the part where the compiler rejects the constructor you thought you were using. In our case, we wanted to use the Widget(WidgetOptions const* options)
constructor.
// clang no known conversion for argument 1 from 'WidgetOptions' to 'const WidgetOptions*' // gcc no known conversion from 'WidgetOptions' to 'const WidgetOptions *' for 1st argument; take the address of the argument with & // msvc cannot convert argument 1 from 'WidgetOptions' to 'const WidgetOptions *'
The problem is that we passed a WidgetÂOptions
object, but the constructor wanted a const WidgetÂOptions*
pointer. We should have passed a pointer to the options rather than the options itself. The gcc compiler even guessed that that was what you intended.
So that’s the trick: Read the start of the error message to find out what went wrong, and then skip over all the intermediate steps to find the line of code you wrote that caused the error. Use your understanding of what your code was trying to do to understand why the compiler couldn’t do it. In our case, we were trying to construct a shared Widget
, and our attention was drawn to the inability to call the constructor correctly.
Bonus chatter: The library authors could do us a solid here and add a special diagnostic to identify the problem sooner. They could put something at the start of make_shared
like this:
static_assert(std::is_constructible_v<_Tp, _Args&&>, "Cannot construct the object from the provided parameters. " "See subsequent error messages for details.");
This adds a message to the error spew that helps guide the developer to the part of the subsequent error message that they should focus on.
0 comments