I am trying to initialize a std::map
with an initializer list (in production, this is a member initializer of a class, but my minimal failing example is below). Given
#include <map>
struct Cfg {};
struct Alg
{
explicit Alg(Cfg const&) {}
};
using MyMap = std::map<int, Alg>;
int main()
{
Cfg cfg;
MyMap m = {
{1, {cfg}}, // error #1
{2, cfg}, // error #2
{3, Alg(cfg)}, // works fine
};
return 0;
}
When compiling, error #1 is:
foo.cc: In function ‘int main()’:
foo.cc:22:5: error: converting to ‘const Alg’ from initializer list would use
explicit constructor ‘Alg::Alg(const Cfg&)’
};
^
This is pretty straightforward. Passing a Cfg
to the initializer requires conversion and the explicit constructor prohibits it.
Error #2 is
foo.cc: In function ‘int main()’:
foo.cc:22:5: error: converting to ‘std::pair<const int, Alg>’ from initializer list would use explicit constructor ‘constexpr std::pair<_T1, _T2>::pair(_U1&&, _U2&&) [with _U1 = int; _U2 = Cfg&; typename std::enable_if<(std::_PCC<true, _T1, _T2>::_MoveConstructiblePair<_U1, _U2>() && (! std::_PCC<true, _T1, _T2>::_ImplicitlyMoveConvertiblePair<_U1, _U2>())), bool>::type <anonymous> = 0; _T1 = const int; _T2 = Alg]’
};
^
This is a little more confusing. I think the error talks about implicitly invoking an explicit std::pair
constructor; reading the documentation for std::pair
, I get lost in which constructors are explicit when.
The third case is explicit construction. For reasons of maintainability, I'd rather not do that.
It seems like I've read that many initializer list counter-intuitive issues are solvable by adding more braces (gross simplification) but I confess I'm lost at this point.
If I remove the explicit
qualification of the Alg
constructor, all three cases compile. However, I'm not sure it makes sense to provide implicit conversion just to simplify an initializer list.
Is there a way to initialize my map elements without explicitly constructing the Alg
members? Using g++ 7.3
As far as I know, there is no way around specifying the Alg
type. This is the intention of explicit
constructors anyhow. Just for the sake of [I don't know what to say here], you can nevertheless invoke std::pair
's in-place constructor like the following.
MyMap m{{std::piecewise_construct, std::forward_as_tuple(1), std::tie(cfg)}};
This way, you don't have to type Alg
, which kind of answers your questions. Besides, do the above only if you hate yourself and your co-workers.
Note: the in-place constructor of std::pair
is actually present to allow for non-copyable, non-movable types.