strange compile problem regarding const-ref v.s. perfect forwarding

Discussion in 'C++' started by huili80@gmail.com, Oct 29, 2012.

  1. Guest

    Can anyone please tell me what I'm doing wrong in the stripped-down code below?
    I can't figure out the cause of failure when compiling, tried both clang++4.1 and g++4.8 with -std=c++11.
    Thanks very much!!

    ----------------------------------------------------
    #include <utility>

    namespace test {

    template < typename D > struct base{};
    struct MyP : base<MyP> { explicit MyP(int){} };

    template < typename T >
    struct remove_cvr
    {
    typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type;
    };

    template < typename... P >
    struct array_impl;

    // single element, recursive root
    template < typename P >
    struct array_impl<P>
    {
    P head_;
    template < typename Q > explicit array_impl(Q&& q):head_(q){} // fail to compile, why?
    };

    template < typename P, typename... T >
    struct array_impl<P,T...>
    {
    P head_; array_impl<T...> tail_;
    template < typename Q, typename... U > explicit array_impl(Q&& q, U&&... u):head_(std::forward<Q>(q)),tail_(std::forward<U>(u)...) {}
    };

    template < typename... P >
    struct array : public array_impl<P...>, public base<array<P...>>
    {
    typedef array_impl<P...> impl_base;
    template < typename... Q > explicit array(Q&&... q):impl_base(std::forward<Q>(q)...){}
    };

    template < typename... P >
    array<typename remove_cvr<P>::type...> make_array(P&&... p)
    {
    return array<typename remove_cvr<P>::type...>(std::forward<P>(p)...);
    }

    //// compiles fine if not trying to forward
    //template < typename... P >
    //array<typename remove_cvr<P>::type...> make_array(const P&... p)
    //{
    // return array<typename remove_cvr<P>::type...>(p...);
    //}

    }

    int main(int argc, const char * argv[])
    {
    using namespace test;

    // make_array(MyP(3.4)); // compiles fine
    make_array(make_array(MyP(3.4))); // failed both g++4.8 and clang++4.1 (mac osx 10.8.2)

    return 0;
    }
    , Oct 29, 2012
    #1
    1. Advertising

  2. FredK Guest

    On Monday, October 29, 2012 9:59:13 AM UTC-7, wrote:
    > Can anyone please tell me what I'm doing wrong in the stripped-down code below? I can't figure out the cause of failure when compiling, tried both clang++4.1 and g++4.8 with -std=c++11. Thanks very much!! ---------------------------------------------------- #include <utility> namespace test { template < typename D > struct base{}; struct MyP : base<MyP> { explicit MyP(int){} }; template < typename T > struct remove_cvr { typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type; }; template < typename... P > struct array_impl; // single element, recursive root template < typename P > struct array_impl<P> { P head_; template < typename Q > explicit array_impl(Q&& q):head_(q){} // fail to compile, why? }; template < typename P, typename... T > struct array_impl<P,T...> { P head_; array_impl<T...> tail_; template < typename Q, typename... U > explicit array_impl(Q&& q, U&&... u):head_(std::forward<Q>(q)),tail_(std::forward<U>(u)...){} }; template < typename... P > struct array : public array_impl<P...>, public base<array<P...>> { typedef array_impl<P...> impl_base; template < typename... Q > explicit array(Q&&... q):impl_base(std::forward<Q>(q)...){} }; template < typename... P > array<typename remove_cvr<P>::type...> make_array(P&&... p) { return array<typename remove_cvr<P>::type...>(std::forward<P>(p)...); } //// compiles fine if not trying to forward //template < typename... P > //array<typename remove_cvr<P>::type...> make_array(const P&... p) //{ // return array<typename remove_cvr<P>::type...>(p...); //} } int main(int argc, const char * argv[]) { using namespace test; // make_array(MyP(3.4)); // compiles fine make_array(make_array(MyP(3.4))); // failed both g++4.8 and clang++4.1 (mac osx 10.8.2) return 0; }



    It would help if you told us what the error message was, and where it occurred.
    FredK, Oct 29, 2012
    #2
    1. Advertising

  3. Guest

    Sure, here are the full output from compiling, for both g++ and clang++.

    With g++ 4.8:
    --------------------
    main.cpp: In instantiation of 'test::array_impl<P>::array_impl(Q&&) [with Q = test::array<test::MyP>&; P = test::MyP]':
    main.cpp:36:88: required from 'test::array<P>::array(Q&& ...) [with Q = {test::array<test::MyP>&}; P = {test::MyP}]'
    main.cpp:22:63: required from 'test::array_impl<P>::array_impl(Q&&) [with Q = test::array<test::MyP>; P = test::array<test::MyP>]'
    main.cpp:36:88: required from 'test::array<P>::array(Q&& ...) [with Q = {test::array<test::MyP>}; P = {test::array<test::MyP>}]'
    main.cpp:42:72: required from 'test::array<typename test::remove_cvr<P>::type ...> test::make_array(P&& ...) [with P = {test::array<test::MyP>}; typename test::remove_cvr<P>::type = <type error>]'
    main.cpp:59:36: required from here
    main.cpp:22:63: error: no matching function for call to 'test::MyP::MyP(test::array<test::MyP>&)'
    template < typename Q > explicit array_impl(Q&& q):head_(q){} // fail to compile, why?
    ^
    main.cpp:22:63: note: candidates are:
    main.cpp:6:35: note: test::MyP::MyP(int)
    struct MyP : base<MyP> { explicit MyP(int){} };
    ^
    main.cpp:6:35: note: no known conversion for argument 1 from 'test::array<test::MyP>' to 'int'
    main.cpp:6:8: note: constexpr test::MyP::MyP(const test::MyP&)
    struct MyP : base<MyP> { explicit MyP(int){} };
    ^
    main.cpp:6:8: note: no known conversion for argument 1 from 'test::array<test::MyP>' to 'const test::MyP&'
    main.cpp:6:8: note: constexpr test::MyP::MyP(test::MyP&&)
    main.cpp:6:8: note: no known conversion for argument 1 from 'test::array<test::MyP>' to 'test::MyP&&'
    mbpro:test huil$
    ------------------

    From clang++4.1:

    ------------------
    main.cpp:22:56: error: no matching constructor for initialization of 'test::MyP'
    template < typename Q > explicit array_impl(Q&& q):head_(q){} // fail to compile, why?
    ^ ~
    main.cpp:36:57: note: in instantiation of function template specialization
    'test::array_impl<test::MyP>::array_impl<test::array<test::MyP> &>' requested here
    template < typename... Q > explicit array(Q&&... q):impl_base(std::forward<Q>(q)...){}
    ^
    main.cpp:22:56: note: in instantiation of function template specialization
    'test::array<test::MyP>::array<test::array<test::MyP> &>' requested here
    template < typename Q > explicit array_impl(Q&& q):head_(q){} // fail to compile, why?
    ^
    main.cpp:36:57: note: in instantiation of function template specialization 'test::array_impl<test::array<test::MyP>
    >::array_impl<test::array<test::MyP> >' requested here

    template < typename... Q > explicit array(Q&&... q):impl_base(std::forward<Q>(q)...){}
    ^
    main.cpp:42:12: note: in instantiation of function template specialization 'test::array<test::array<test::MyP>
    >::array<test::array<test::MyP> >' requested here

    return array<typename remove_cvr<P>::type...>(std::forward<P>(p)...);
    ^
    main.cpp:59:5: note: in instantiation of function template specialization 'test::make_array<test::array<test::MyP> >' requested
    here
    make_array(make_array(MyP(3.4))); // failed both g++4.8 and clang++4.1 (mac osx 10.8.2)
    ^
    main.cpp:6:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from
    'test::array<test::MyP>' to 'const test::MyP' for 1st argument;
    struct MyP : base<MyP> { explicit MyP(int){} };
    ^
    main.cpp:6:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from
    'test::array<test::MyP>' to 'test::MyP' for 1st argument;
    struct MyP : base<MyP> { explicit MyP(int){} };
    ^
    main.cpp:6:35: note: candidate constructor not viable: no known conversion from 'test::array<test::MyP>' to 'int' for 1st argument;
    struct MyP : base<MyP> { explicit MyP(int){} };
    ^
    1 error generated.
    ------------------------
    , Oct 29, 2012
    #3
  4. Guest

    Thanks for replying. The reason to use to template constructor is to be able to perfectly forward arguments. Your solution doesn't allow array_impl to be constructed from lvalues, does it?

    I just figured out a way to fix my code. I need to selectively enable the template constructors only when types are compatible (in my case, exactly the same up to cv qualifier and reference, lvalue or rvalue), hence this:

    template < typename P >
    struct array_impl<P>
    {
    P head_;
    template < typename Q, class = typename std::enable_if<std::is_same<P,typename remove_cvr<Q>::type>::value>::type >
    explicit array_impl(Q&& q):head_(q){}
    };

    And of course the equivalent for the variadic one (slightly more complicated, so omitted here).
    Everything works now.
    , Oct 29, 2012
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Scott Meyers
    Replies:
    6
    Views:
    959
    Scott Meyers
    Dec 5, 2010
  2. Scott Meyers
    Replies:
    20
    Views:
    1,089
    itaj sherman
    Mar 16, 2011
  3. Andrew Tomazos
    Replies:
    1
    Views:
    975
    Andrew Tomazos
    Dec 23, 2011
  4. Alexis Nikichine

    Perfect function forwarding

    Alexis Nikichine, Dec 27, 2005, in forum: Javascript
    Replies:
    6
    Views:
    152
    Alexis Nikichine
    Dec 28, 2005
  5. dervih
    Replies:
    3
    Views:
    497
    dervih
    Jul 13, 2012
Loading...

Share This Page