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


H

huili80

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;
}
 
Ad

Advertisements

F

FredK

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.
 
H

huili80

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 said:
::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 said:
::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.
------------------------
 
Ad

Advertisements

H

huili80

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top