[move] You can do it better: implementing push_back and handling implicit conversions

Hi to all, Are you a C++ expert? Do you like Boost.Move? I have a challenge for you. During Boost.Move review, Jeffrey Lee Hellrung suggested adding "techniques to capture (implicitly created) rvalues with emulated rvalue references in C++03, e.g., for push_back, when you have knowledge of the types of objects you want to capture". I've worked on this problem these days and the solution is less than obvious (several compilers have has problems with ::boost::is_convertible and non-copyable types). I found a solution (attached test and required boost/move/move.hpp header) after several tries but it's complex (it needs several overloads and enable_if tricks) but I'm sure boosters will find a easier solution ;-) Current solution seems to work on these compilers: MSVC 7.1, 8.0, 9.0, Intel 11.0, GCC 4.3.4, 4.4, 4.4 c++0x mode, 4.5, 4.5 c++0x mode. Problem: MSVC 10.0 (Visual 2010) with real rvalue references seems to have bugs and does not compile the test. I can't find a workaround, a future Service Pack might solve these errors, but maybe we should stick to emulation code in this compiler, unless someone could shed some light on this, of course. Waiting your proposals, Ion

[Ion Gaztañaga]
Problem: MSVC 10.0 (Visual 2010) with real rvalue references seems to have bugs and does not compile the test. I can't find a workaround, a future Service Pack might solve these errors, but maybe we should stick to emulation code in this compiler, unless someone could shed some light on this, of course.
Can you provide a minimal test case, demonstrating just the bug in question? Thanks, Stephan T. Lavavej Visual C++ Libraries Developer

[Ion Gaztañaga]
Problem: MSVC 10.0 (Visual 2010) with real rvalue references seems to have bugs and does not compile the test.
I don't think that the compile error of MSVC 10.0 triggered by the statement container<conversion_target_movable> c; is entirely unique to version 10.0, it's only triggered more frequently (and especially I think it is completely unrelated to rvalue references). IIRC, the same compile error will be triggered by the following code class __declspec(dllexport) derived_class : public container<conversion_target_movable> { ... }; in earlier versions of MSVC. I think that this error is caused by MSVC instantiating every single non-template member function of the "container<conversion_target_movable>" class, whether it is actually used or not. I have to admit that I understand why MSVC does this in case of __declspec(dllexport). However, triggering the same behavior by the statement "container<conversion_target_movable> c;" probably really qualifies as bug.
I can't find a workaround, a future Service Pack might solve these errors, but maybe we should stick to emulation code in this compiler, unless someone could shed some light on this, of course.
As I think this is unrelated to rvalue references, I don't see how sticking to emulation code should help with this. A workaround for your example is to turn the non-template member function into a template member function to avoid that MSVC 10 instantiates the member function which is not applicable to non-copyable element types. template<class TT> void push_back(BOOST_MOVE_CATCH_CONST(TT) x) { return priv_push_back(static_cast<const TT&>(x)); } A more challenging workaround would be to selectively deactivate all non-applicable member functions by meta-programming. [Stephan T. Lavavej]
Can you provide a minimal test case, demonstrating just the bug in question?
Is the above description of the issue clear enough, or do you still need an explicit minimal test case? Regards, Thomas

[STL]
Can you provide a minimal test case, demonstrating just the bug in question?
[Thomas Klimpel]
Is the above description of the issue clear enough, or do you still need an explicit minimal test case?
The latter, please. I just need a tiny source file with as little extraneous machinery as possible, a command line, its output (so I can verify that I'm seeing what you're seeing), and a description of what you expect should happen (with Working Paper citations if you want to be super helpful). Then I can figure out what's going on myself, or route it to the compiler team. Thanks, STL

[STL]
Can you provide a minimal test case, demonstrating just the bug in question?
[Thomas Klimpel]
Is the above description of the issue clear enough, or do you still need an explicit minimal test case?
[STL]
The latter, please. I just need a tiny source file with as little extraneous machinery as possible, a command line, its output (so I can verify that I'm seeing what you're seeing), and a description of what you expect should happen (with Working Paper citations if you want to be super helpful). Then I can figure out what's going on myself, or route it to the compiler team.
Find attached a test case showing the scenario I described. However, it turns out that I was mislead by the error message when I wrote
I don't think that the compile error of MSVC 10.0 triggered by the statement
container<conversion_target_movable> c;
is entirely unique to version 10.0, it's only triggered more frequently (and especially I think it is completely unrelated to rvalue references).
because the "non-applicable member function" is actually explicitly used/called some lines below the statement that the compiler claims to be the offending one. So putting aside the question whether the "non-applicable member function" should actually be applicable because of some tricky standard wordings, one bug here is that the error message emitted by the compiler doesn't contain the line number of the offending statement, but only the line number of the declaration of the variable later used in the offending statement.
IIRC, the same compile error will be triggered by the following code
class __declspec(dllexport) derived_class : public container<conversion_target_movable> { ... };
This code actually really triggers the mentioned compile error. Standard wording can't help us here, because the standard doesn't say anything about dynamic link libraries. So I wouldn't see this as a bug, even if it is annoying at times. Regards, Thomas

[Thomas Klimpel]
Find attached a test case showing the scenario I described. This code actually really triggers the mentioned compile error. Standard wording can't help us here, because the standard doesn't say anything about dynamic link libraries. So I wouldn't see this as a bug, even if it is annoying at times.
By design. C:\Temp>type meow.cpp template <class T> struct container { T m; void forward_member_function() { m.member_function(); } }; struct b { }; #ifdef BOOM struct __declspec(dllexport) my_class : public container<b> { }; #else struct my_class : public container<b> { }; #endif C:\Temp>cl /EHsc /nologo /W4 /c meow.cpp meow.cpp C:\Temp>cl /EHsc /nologo /W4 /c /DBOOM meow.cpp meow.cpp meow.cpp(5) : error C2039: 'member_function' : is not a member of 'b' meow.cpp(9) : see declaration of 'b' meow.cpp(4) : while compiling class template member function 'void container<T>::forward_member_function(void)' with [ T=b ] meow.cpp(12) : see reference to class template instantiation 'container<T>' being compiled with [ T=b ] http://msdn.microsoft.com/en-us/library/81h27t8c.aspx says: "Inheritance and Exportable Classes All base classes of an exportable class must be exportable." http://msdn.microsoft.com/en-us/library/twa2aw10.aspx says: "the compiler changed the semantics of dllexport when it is applied to a class that has one or more base-classes and when one or more of the base classes is a specialization of a class template. In this case, the compiler implicitly applies dllexport to the specializations of class templates." (This page is a little confusing because it was written when VS 2003 was new.) When my_class is dllexported, its base class container<b> is explicitly instantiated and compiled into the DLL. That's why container<b>::forward_member_function() is being instantiated and subsequently exploding. I'm not an expert when it comes to DLLs, but I can't imagine another way for this to physically work. In the absence of dllexport, the compiler plays by the Standard rules that allow std::list<T> to work when T doesn't have op<(), as long as std::list<T>::sort() isn't called. Stephan T. Lavavej Visual C++ Libraries Developer

El 08/03/2011 23:10, Stephan T. Lavavej escribió:
Can you provide a minimal test case, demonstrating just the bug in question?
This compiles fine in GCC 4.4 and GCC 4.5 in -std=gnu++0x mode, but fails on visual, since conversion provokes the instantiation of conversion_target_movable's (private) copy constructor. I hope this helps: #include <utility> #include <memory> class conversion_source { public: conversion_source(){} }; class conversion_target_movable { conversion_target_movable (const conversion_target_movable&); conversion_target_movable operator=(const conversion_target_movable&); public: conversion_target_movable(){} conversion_target_movable(conversion_source){} conversion_target_movable(conversion_target_movable &&){} conversion_target_movable &operator=(conversion_target_movable &&) { return *this; } }; template<class T> class container { public: void push_back(const T & x) { return priv_push_back(static_cast<const T&>(x)); } void push_back(T &&x) { return priv_push_back(::std::move(x)); } private: template<class U> void priv_push_back(U &&x) { ::new(&t) T(::std::forward<U>(x)); } T t; }; int main() { container<conversion_target_movable> c; { conversion_source x; c.push_back(x); } { const conversion_source x; c.push_back(x); } { c.push_back(conversion_source()); } return 0; } the output is: ------ Build started: Project: move_bug, Configuration: Debug Win32 ------ 1> move_bug.cpp 1>test.cpp(36): error C2248: 'conversion_target_movable::conversion_target_movable' : cannot access private member declared in class 'conversion_target_movable' 1> test.cpp(13) : see declaration of 'conversion_target_movable::conversion_target_movable' 1> test.cpp(12) : see declaration of 'conversion_target_movable' 1> test.cpp(28) : see reference to function template instantiation 'void container<T>::priv_push_back<const T&>(U)' being compiled 1> with 1> [ 1> T=conversion_target_movable, 1> U=const conversion_target_movable & 1> ] 1> test.cpp(28) : while compiling class template member function 'void container<T>::push_back(const T &)' 1> with 1> [ 1> T=conversion_target_movable 1> ] 1> test.cpp(44) : see reference to class template instantiation 'container<T>' being compiled 1> with 1> [ 1> T=conversion_target_movable 1> ] ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
participants (3)
-
Ion Gaztañaga
-
Stephan T. Lavavej
-
Thomas Klimpel