Opened 10 years ago

Closed 4 years ago

Last modified 4 years ago

#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)

comment:1 by zadirion@…, 10 years ago

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.

comment:2 by Eric Niebler, 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.

in reply to:  2 comment:3 by zadirion@…, 10 years ago

Replying to eric_niebler:

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.

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:4 by anonymous, 10 years ago

How come this hasn't been fixed yet? This is pretty serious.

comment:5 by Eric Niebler, 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.

in reply to:  1 comment:6 by anonymous, 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:7 by Andrey <nikolay@…>, 9 years ago

Any plans when this issue will be fixed?

comment:8 by asaliheddine@…, 9 years ago

Hi Guys

is there any update on this issue? any fix for VS2012?

Thanks Ahmed

comment:9 by abhinuke@…, 8 years ago

Hi Guys,

Is there any update on this issue? Any fix for VS2013?

Thanks, Abhishek D

comment:10 by Michel Morin, 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 Jim King <jim.king@…>, 7 years ago

Any chance we can get this fix into boost 1.60? I found it solves the issue as well.

comment:12 by anonymous, 6 years ago

Is this fixed yet?

comment:13 by anonymous, 5 years ago

Had to use workaround

using namespace boost::assign; std::vector<int> data; data += 1,2,3;

comment:14 by James E. King, III, 4 years ago

Owner: changed from Thorsten Ottosen to James E. King, III

comment:15 by James E. King, III, 4 years ago

Resolution: duplicate
Status: newclosed

See trac-5419

comment:16 by James E. King, III, 4 years ago

Milestone: To Be DeterminedBoost 1.69
Version: Boost Development TrunkBoost 1.53.0
Note: See TracTickets for help on using tickets.