Modify

Opened 5 years ago

Last modified 16 months ago

#7364 new Bugs

ambiguity error constructing std::vector from assign::list_of

Reported by: Eric Niebler Owned by: Thorsten Ottosen
Milestone: To Be Determined Component: assign
Version: Boost Development Trunk 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.

Attachments (0)

Change History (12)

comment:1 Changed 5 years ago by zadirion@…

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 Changed 5 years ago by 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.

comment:3 in reply to:  2 Changed 5 years ago by zadirion@…

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 Changed 5 years ago by anonymous

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

comment:5 Changed 5 years ago by Eric Niebler

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 in reply to:  1 Changed 4 years ago by anonymous

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 Changed 4 years ago by Andrey <nikolay@…>

Any plans when this issue will be fixed?

comment:8 Changed 4 years ago by asaliheddine@…

Hi Guys

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

Thanks Ahmed

comment:9 Changed 3 years ago by abhinuke@…

Hi Guys,

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

Thanks, Abhishek D

comment:10 Changed 3 years ago by Michel Morin

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 Changed 2 years ago by Jim King <jim.king@…>

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

comment:12 Changed 16 months ago by anonymous

Is this fixed yet?

Modify Ticket

Change Properties
Set your email in Preferences
Action
as new The owner will remain Thorsten Ottosen.

Add Comment


E-mail address and name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.