The problem with the one-argument version becomes much more obvious when you replace list_of<A>(...)
by list_of(A(...))
: the compiler error them becomes (GCC):
list.cc: In function ‘int main()’:
list.cc:18:30: error: call of overloaded ‘A(boost::assign_detail::generic_list<int>&)’ is ambiguous
list_of (A (list_of (1) (2)));
^
list.cc:18:30: note: candidates are:
list.cc:11:3: note: A::A(std::vector<int>, int, int)
A(std::vector<int> _nodes, int _min_version = 0, int _max_version = INT_MAX)
^
list.cc:6:8: note: A::A(const A&)
struct A {
^
list.cc:6:8: note: A::A(A&&)
And this makes sense, sort of: the generic untyped list* claims to be convertible to any type; it does not (and probably cannot) use SFINAE to reject types to which the conversion wouldn't work.
In your example, a conversion from the untyped list to A
is a better match than a conversion from the untyped list to std::vector<int>
to A
. That conversion, then, of course does not work.
When you pass the second argument, the compiler knows that you cannot possibly be attempting to call the copy constructor.
My personal preference would be to add a dummy argument to the constructor, similar to the standard's use of piecewise_construct_t
:
struct from_vector { };
struct A {
...
A(std::vector<int> _nodes, ...) { ... }
A(from_vector, std::vector<int> _nodes, ...) : this(_nodes, ...) { }
};
You can then write
list_of<A>( from_vector(), list_of (1) (2) )
* When I say "untyped list", I do of course realise that Boost has actually defined a type for that list, and that C++ expressions always have a type.