#7364 closed Bugs (duplicate)
ambiguity error constructing std::vector from assign::list_of
| Reported by: | Eric Niebler | Owned by: | James E. King, III | 
|---|---|---|---|
| Milestone: | Boost 1.69 | Component: | assign | 
| Version: | Boost 1.53.0 | Severity: | Problem | 
| Keywords: | Cc: | 
Description
The problem is due to the addition of rvalue-reference container constructors. The following fails to compile with vc10:
#include <vector>
#include <boost/assign/list_of.hpp>
int main()
{
    std::vector<int> i(boost::assign::list_of(1));
}
The error is:
1>c:\boost\org\trunk\libs\proto\scratch\main.cpp(6): error C2668: 'std::vector<_Ty>::vector' : ambiguous call to overloaded function 1> with 1> [ 1> _Ty=int 1> ] 1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector(593): could be 'std::vector<_Ty>::vector(std::vector<_Ty> &&)' 1> with 1> [ 1> _Ty=int 1> ] 1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector(515): or 'std::vector<_Ty>::vector(unsigned int)' 1> with 1> [ 1> _Ty=int 1> ] 1> while trying to match the argument list '(boost::assign_detail::generic_list<T>)' 1> with 1> [ 1> T=int 1> ] 1> 1>Build FAILED.
Change History (17)
follow-up: 6 comment:1 by , 10 years ago
follow-up: 3 comment:2 by , 10 years ago
Below is an implementation of list_of that addresses the issue for C++11 compilers.
#include <iostream>
#include <iterator>
#include <algorithm>
#include <deque>
#include <vector>
#include <utility>
#include <type_traits>
template<typename T>
using uncvref = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
template<typename T>
struct generic_list;
template<typename T>
generic_list<uncvref<T>> list_of(T && t);
template<typename T>
struct generic_list
{
private:
    std::deque<T> values;
    friend generic_list<T> list_of<>(T && t);
    friend generic_list<T> list_of<>(T & t);
    friend generic_list<T> list_of<>(T const & t);
    generic_list() = default;
public:
    generic_list(generic_list &&) = default;
    generic_list(generic_list const &) = delete;
    generic_list &operator=(generic_list &&) = delete;
    generic_list &operator=(generic_list const &) = delete;
    template<typename U>
    generic_list & operator()(U && t) noexcept(noexcept(values.push_back(static_cast<U &&>(t))))
    {
        values.push_back(static_cast<U &&>(t));
        return *this;
    }
    template<typename Container, typename = decltype(Container(std::make_move_iterator(values.begin()), std::make_move_iterator(values.end())))>
    operator Container() noexcept(noexcept(Container(Container(std::make_move_iterator(values.begin()), std::make_move_iterator(values.end())))))
    {
        return Container(std::make_move_iterator(values.begin()), std::make_move_iterator(values.end()));
    }
};
template<typename T>
inline generic_list<uncvref<T>> list_of(T && t)
{
    return std::move(generic_list<uncvref<T>>()(static_cast<T &&>(t)));
}
struct moveable
{
    moveable() = default;
    moveable(moveable &&) = default;
    moveable(moveable const &) = delete;
    moveable &operator=(moveable &&) = default;
    moveable &operator=(moveable const &) = delete;
};
int main()
{
    std::vector<int> i(list_of(1));
    std::copy(i.begin(), i.end(), std::ostream_iterator<int>(std::cout, "\n"));
    std::vector<moveable> j = list_of(moveable())(moveable());
}
The trick to fixing the ambiguity error is the defaulted template parameter on operator Container that only allows the conversion if the container can be constructed from a pair of iterators.
HTH.
comment:3 by , 10 years ago
Replying to eric_niebler:
Below is an implementation of
list_ofthat addresses the issue for C++11 compilers.#include <iostream> #include <iterator> #include <algorithm> #include <deque> #include <vector> #include <utility> #include <type_traits> template<typename T> using uncvref = typename std::remove_cv<typename std::remove_reference<T>::type>::type; template<typename T> struct generic_list; template<typename T> generic_list<uncvref<T>> list_of(T && t); template<typename T> struct generic_list { private: std::deque<T> values; friend generic_list<T> list_of<>(T && t); friend generic_list<T> list_of<>(T & t); friend generic_list<T> list_of<>(T const & t); generic_list() = default; public: generic_list(generic_list &&) = default; generic_list(generic_list const &) = delete; generic_list &operator=(generic_list &&) = delete; generic_list &operator=(generic_list const &) = delete; template<typename U> generic_list & operator()(U && t) noexcept(noexcept(values.push_back(static_cast<U &&>(t)))) { values.push_back(static_cast<U &&>(t)); return *this; } template<typename Container, typename = decltype(Container(std::make_move_iterator(values.begin()), std::make_move_iterator(values.end())))> operator Container() noexcept(noexcept(Container(Container(std::make_move_iterator(values.begin()), std::make_move_iterator(values.end()))))) { return Container(std::make_move_iterator(values.begin()), std::make_move_iterator(values.end())); } }; template<typename T> inline generic_list<uncvref<T>> list_of(T && t) { return std::move(generic_list<uncvref<T>>()(static_cast<T &&>(t))); } struct moveable { moveable() = default; moveable(moveable &&) = default; moveable(moveable const &) = delete; moveable &operator=(moveable &&) = default; moveable &operator=(moveable const &) = delete; }; int main() { std::vector<int> i(list_of(1)); std::copy(i.begin(), i.end(), std::ostream_iterator<int>(std::cout, "\n")); std::vector<moveable> j = list_of(moveable())(moveable()); }The trick to fixing the ambiguity error is the defaulted template parameter on
operator Containerthat only allows the conversion if the container can be constructed from a pair of iterators.HTH.
I must point out the fix you posted does not compile in Visual Studio 2012. The templated using syntax and the = default and = delete modifiers are not implemented in VS2012
comment:5 by , 10 years ago
To be fair to Thorsten, I think this is impossible to fix in C++98. At least, I wasn't able to think of a way.
comment:6 by , 9 years ago
Replying to zadirion@…:
Adding a to_container(dummy) call on the second list_of fixes the issue
or more succinctly convert_to_container< container_type >().
comment:8 by , 9 years ago
Hi Guys
is there any update on this issue? any fix for VS2012?
Thanks Ahmed
comment:9 by , 8 years ago
Hi Guys,
Is there any update on this issue? Any fix for VS2013?
Thanks, Abhishek D
comment:10 by , 8 years ago
My patch (assign_cxx0x.patch) in #5419 fixes this problem on clang and gcc. I've not tested with VS, but I believe it works fine on VS 2013.
comment:11 by , 7 years ago
Any chance we can get this fix into boost 1.60? I found it solves the issue as well.
comment:13 by , 5 years ago
Had to use workaround
using namespace boost::assign; std::vector<int> data; data += 1,2,3;
comment:14 by , 4 years ago
| Owner: | changed from to | 
|---|
comment:16 by , 4 years ago
| Milestone: | To Be Determined → Boost 1.69 | 
|---|---|
| Version: | Boost Development Trunk → Boost 1.53.0 | 


Ok, since my report was closed as a duplicate, I'll be letting people know here that a temporary fix for this is the following:
Adding a to_container(dummy) call on the second list_of fixes the issue, where dummy is a dummy object of the same type as the container.