Merge branch 'develop'

This commit is contained in:
Ion Gaztañaga
2014-02-23 14:11:13 +01:00
7 changed files with 496 additions and 129 deletions

View File

@@ -7,7 +7,7 @@
[library Boost.Move
[quickbook 1.5]
[authors [Gaztanaga, Ion]]
[copyright 2008-2012 Ion Gaztanaga]
[copyright 2008-2014 Ion Gaztanaga]
[id move]
[dirname move]
[purpose Move semantics]
@@ -327,127 +327,92 @@ C++03 compilers. In compilers with rvalue references perfect forwarding is achie
[endsect]
[/[section:perfect_forwarding Perfect Forwarding]
/
/Consider writing a generic factory function that returns a std::shared_ptr for a newly
/constructed generic type. Factory functions such as this are valuable for encapsulating
/and localizing the allocation of resources. Obviously, the factory function must accept
/exactly the same sets of arguments as the constructors of the type of objects constructed.
/Today this might be coded as:
/
/[c++]
/
/ template <class T>
/ std::shared_ptr<T>
/ factory() // no argument version
/ {
/ return std::shared_ptr<T>(new T);
/ }
/
/ template <class T, class A1>
/ std::shared_ptr<T>
/ factory(const A1& a1) // one argument version
/ {
/ return std::shared_ptr<T>(new T(a1));
/ }
/
/ // all the other versions
/
/
/In the interest of brevity, we will focus on just the one-parameter version. For example:
/
/ [c++]
/
/ std::shared_ptr<A> p = factory<A>(5);
/
/
/ [*Question]: What if T's constructor takes a parameter by non-const reference?
/
/ In that case, we get a compile-time error as the const-qualifed argument of the factory
/ function will not bind to the non-const parameter of T's constructor.
/
/ To solve that problem, we could use non-const parameters in our factory functions:
/
/ [c++]
/
/ template <class T, class A1>
/ std::shared_ptr<T>
/ factory(A1& a1)
/ {
/ return std::shared_ptr<T>(new T(a1));
/ }
/
/
/ This is much better. If a const-qualified type is passed to the factory, the const will
/ be deduced into the template parameter (A1 for example) and then properly forwarded to
/ T's constructor. Similarly, if a non-const argument is given to factory, it will be
/ correctly forwarded to T's constructor as a non-const. Indeed, this is precisely how
/forwarding applications are coded today (e.g. `std::bind`).
/
/However, consider:
/
/[c++]
/
/ std::shared_ptr<A> p = factory<A>(5); // error
/ A* q = new A(5); // ok
/
/
/This example worked with our first version of factory, but now it's broken: The "5"
/causesthe factory template argument to be deduced as int& and subsequently will not
/bind to the rvalue "5". Neither solution so far is right. Each breaks reasonable and
/common code.
/
/[*Question]: What about overloading on every combination of AI& and const AI&?
/
/This would allow use to handle all examples, but at a cost of an exponential explosion:
/For our two-parameter case, this would require 4 overloads. For a three-parameter factory
/we would need 8 additional overloads. For a four-parameter factory we would need 16, and
/so on. This is not a scalable solution.
/
/Rvalue references offer a simple, scalable solution to this problem:
/
/[c++]
/
/ template <class T, class A1>
/ std::shared_ptr<T>
/ factory(A1&& a1)
/ {
/ return std::shared_ptr<T>(new T(std::forward<A1>(a1)));
/ }
/
/ Now rvalue arguments can bind to the factory parameters. If the argument is const, that
/ fact gets deduced into the factory template parameter type.
/
/ [*Question]: What is that forward function in our solution?
/
/ Like move, forward is a simple standard library function used to express our intent
/ directly and explicitly, rather than through potentially cryptic uses of references.
/ We want to forward the argument a1, so we simply say so.
/
/ Here, forward preserves the lvalue/rvalue-ness of the argument that was passed to factory.
/ If an rvalue is passed to factory, then an rvalue will be passed to T's constructor with
/ the help of the forward function. Similarly, if an lvalue is passed to factory, it is
/ forwarded to T's constructor as an lvalue.
/
/ The definition of forward looks like this:
/
/ [c++]
/
/ template <class T>
/ struct identity
/ {
/ typedef T type;
/ };
/
/ template <class T>
/ T&& forward(typename identity<T>::type&& a)
/ {
/ return a;
/ }
/
/[endsect]
/
/]
[section:move_return Implicit Move when returning a local object]
The C++ standard specifies situations where an implicit move operation is safe and the
compiler can use it in cases were the (Named) Return Value Optimization) can't be used.
The typical use case is when a function returns a named (non-temporary) object by value
and the following code will perfectly compile in C++11:
[c++]
//Even if movable can't be copied
//the compiler will call the move-constructor
//to generate the return value
//
//The compiler can also optimize out the move
//and directly construct the object 'm'
movable factory()
{
movable tmp;
m = ...
//(1) moved instead of copied
return tmp;
};
//Initialize object
movable m(factory());
In compilers without rvalue references and some non-conforming compilers (such as Visual C++ 2010/2012)
the line marked with `(1)` would trigger a compilation error because `movable` can't be copied. Using a explicit
`::boost::move(tmp)` would workaround this limitation but it would code suboptimal in C++11 compilers
(as the compile could not use (N)RVO to optimize-away the copy/move).
[*Boost.Move] offers an additional macro called [macroref BOOST_MOVE_RET BOOST_MOVE_RET] that can be used to
alleviate this problem obtaining portable move-on-return semantics. Let's use the previously presented
movable-only `movable` class with classes `copyable` (copy-only type), `copy_movable` (can be copied and moved) and
`non_copy_movable` (non-copyable and non-movable):
[import ../example/copymovable.hpp]
[copy_movable_definition]
and build a generic factory function that returns a newly constructed value or a reference to an already
constructed object.
[import ../example/doc_move_return.cpp]
[move_return_example]
[*Caution]: When using this macro in a non-conforming or C++03
compilers, a move will be performed even if the C++11 standard does not allow it
(e.g. returning a static variable). The user is responsible for using this macro
only used to return local objects that met C++11 criteria. E.g.:
[c++]
struct foo
{
copy_movable operator()() const
{
//ERROR! The Standard does not allow implicit move returns when the object to be returned
//does not met the criteria for elision of a copy operation (such as returning a static member data)
//In C++03 compilers this will MOVE resources from cm
//In C++11 compilers this will COPY resources from cm
//so DON'T use use BOOST_MOVE_RET without care.
return BOOST_MOVE_RET(copy_movable, cm);
}
static copy_movable cm;
};
[*Note]: When returning a temporary object `BOOST_MOVE_REF` is not needed as copy ellision rules will work on
both C++03 and C++11 compilers.
[c++]
//Note: no BOOST_MOVE_RET
movable get_movable()
{ return movable(); }
copy_movable get_copy_movable()
{ return copy_movable(); }
copyable get_copyable()
{ return copyable(); }
[endsect]
[section:move_iterator Move iterators]
@@ -790,6 +755,12 @@ Many thanks to all boosters that have tested, reviewed and improved the library.
[section:release_notes Release Notes]
[section:release_notes_boost_1_56_00 Boost 1.56 Release]
* Added [macroref BOOST_MOVE_RET BOOST_MOVE_RET].
[endsect]
[section:release_notes_boost_1_55_00 Boost 1.55 Release]
* Fixed bugs [@https://svn.boost.org/trac/boost/ticket/7952 #7952],
@@ -797,6 +768,7 @@ Many thanks to all boosters that have tested, reviewed and improved the library.
[@https://svn.boost.org/trac/boost/ticket/8765 #8765],
[@https://svn.boost.org/trac/boost/ticket/8842 #8842],
[@https://svn.boost.org/trac/boost/ticket/8979 #8979].
[endsect]

View File

@@ -13,8 +13,8 @@
#include <boost/move/detail/config_begin.hpp>
//[movable_definition
//header file "copy_movable.hpp"
//[copy_movable_definition
//header file "copymovable.hpp"
#include <boost/move/core.hpp>
//A copy_movable class
@@ -43,6 +43,20 @@ class copy_movable
{ return value_ == 0; }
};
//A copyable-only class
class copyable
{};
//A copyable-only class
class non_copy_movable
{
public:
non_copy_movable(){}
private:
non_copy_movable(const non_copy_movable&);
non_copy_movable& operator=(const non_copy_movable&);
};
//]
#include <boost/move/detail/config_end.hpp>

View File

@@ -0,0 +1,70 @@
//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2014-2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org/libs/move for documentation.
//
//////////////////////////////////////////////////////////////////////////////
#include <boost/move/detail/config_begin.hpp>
//[move_return_example
#include "movable.hpp"
#include "copymovable.hpp"
#include <boost/move/core.hpp>
template<class Type>
struct factory_functor
{
typedef Type return_type;
Type operator()() const
{ Type t; return BOOST_MOVE_RET(Type, t); }
};
struct return_reference
{
typedef non_copy_movable &return_type;
non_copy_movable &operator()() const
{ return ncm; }
static non_copy_movable ncm;
};
non_copy_movable return_reference::ncm;
//A wrapper that locks a mutex while the
//factory creates a new value.
//It must generically move the return value
//if possible both in C++03 and C++11
template <class Factory>
typename Factory::return_type lock_wrapper(Factory f)
{
typedef typename Factory::return_type return_type;
//LOCK();
return_type r = f();
//UNLOCK();
//In C++03: boost::move() if R is not a reference and
//has move emulation enabled. In C++11: just return r.
return BOOST_MOVE_RET(return_type, r);
}
int main()
{
movable m = lock_wrapper(factory_functor<movable> ());
copy_movable cm = lock_wrapper(factory_functor<copy_movable>());
copyable c = lock_wrapper(factory_functor<copyable> ());
non_copy_movable &mr = lock_wrapper(return_reference ());
//<-
(void)m; (void)cm; (void)c; (void)mr;
//->
return 0;
}
//]
#include <boost/move/detail/config_end.hpp>

View File

@@ -168,6 +168,46 @@
const ::boost::rv< TYPE >& \
//
namespace boost {
namespace move_detail {
template <class Ret, class T>
inline typename ::boost::move_detail::enable_if_c
< ::boost::move_detail::is_lvalue_reference<Ret>::value ||
!::boost::has_move_emulation_enabled<T>::value
, T&>::type
move_return(T& x) BOOST_NOEXCEPT
{
return x;
}
template <class Ret, class T>
inline typename ::boost::move_detail::enable_if_c
< !::boost::move_detail::is_lvalue_reference<Ret>::value &&
::boost::has_move_emulation_enabled<T>::value
, ::boost::rv<T>&>::type
move_return(T& x) BOOST_NOEXCEPT
{
return *static_cast< ::boost::rv<T>* >(::boost::move_detail::addressof(x));
}
template <class Ret, class T>
inline typename ::boost::move_detail::enable_if_c
< !::boost::move_detail::is_lvalue_reference<Ret>::value &&
::boost::has_move_emulation_enabled<T>::value
, ::boost::rv<T>&>::type
move_return(::boost::rv<T>& x) BOOST_NOEXCEPT
{
return x;
}
} //namespace move_detail {
} //namespace boost {
#define BOOST_MOVE_RET(RET_TYPE, REF)\
boost::move_detail::move_return< RET_TYPE >(REF)
//
//////////////////////////////////////////////////////////////////////////////
//
// BOOST_MOVABLE_BUT_NOT_COPYABLE
@@ -220,9 +260,12 @@
#elif defined(_MSC_VER) && (_MSC_VER == 1600)
//Standard rvalue binding rules but with some bugs
#define BOOST_MOVE_MSVC_10_MEMBER_RVALUE_REF_BUG
#define BOOST_MOVE_MSVC_AUTO_MOVE_RETURN_BUG
//Use standard library for MSVC to avoid namespace issues as
//some move calls in the STL are not fully qualified.
//#define BOOST_MOVE_USE_STANDARD_LIBRARY_MOVE
#elif defined(_MSC_VER) && (_MSC_VER == 1700)
#define BOOST_MOVE_MSVC_AUTO_MOVE_RETURN_BUG
#endif
#endif
@@ -298,7 +341,6 @@
//
#if !defined(BOOST_MOVE_DOXYGEN_INVOKED)
/// @cond
#define BOOST_RV_REF_2_TEMPL_ARGS(TYPE, ARG1, ARG2)\
TYPE<ARG1, ARG2> && \
@@ -328,10 +370,69 @@
const TYPE & \
//
/// @endcond
#endif //#if !defined(BOOST_MOVE_DOXYGEN_INVOKED)
#if !defined(BOOST_MOVE_MSVC_AUTO_MOVE_RETURN_BUG) || defined(BOOST_MOVE_DOXYGEN_INVOKED)
//!This macro is used to achieve portable move return semantics.
//!The Standard allows implicit move returns when the object to be returned
//!is designated by an lvalue and:
//! - The criteria for elision of a copy operation are met OR
//! - The criteria would be met save for the fact that the source object is a function parameter
//!
//!For C++11 conforming compilers this macros only yields to REF:
//! <code>return BOOST_MOVE_RET(RET_TYPE, REF);</code> -> <code>return REF;</code>
//!
//!For compilers without rvalue references
//!this macro does an explicit move if the move emulation is activated
//!and the return type (RET_TYPE) is not a reference.
//!
//!For non-conforming compilers with rvalue references like Visual 2010 & 2012,
//!an explicit move is performed if RET_TYPE is not a reference.
//!
//! <b>Caution</b>: When using this macro in a non-conforming or C++03
//!compilers, a move will be performed even if the C++11 standard does not allow it
//!(e.g. returning a static variable). The user is responsible for using this macro
//!only used to return local objects that met C++11 criteria.
#define BOOST_MOVE_RET(RET_TYPE, REF)\
(REF)
//
#else //!defined(BOOST_MOVE_MSVC_AUTO_MOVE_RETURN_BUG) || defined(BOOST_MOVE_DOXYGEN_INVOKED)
#include <boost/move/detail/meta_utils.hpp>
namespace boost {
namespace move_detail {
template <class Ret, class T>
inline typename ::boost::move_detail::enable_if_c
< ::boost::move_detail::is_lvalue_reference<Ret>::value
, T&>::type
move_return(T& x) BOOST_NOEXCEPT
{
return x;
}
template <class Ret, class T>
inline typename ::boost::move_detail::enable_if_c
< !::boost::move_detail::is_lvalue_reference<Ret>::value
, Ret && >::type
move_return(T&& t) BOOST_NOEXCEPT
{
return static_cast< Ret&& >(t);
}
} //namespace move_detail {
} //namespace boost {
#define BOOST_MOVE_RET(RET_TYPE, REF)\
boost::move_detail::move_return< RET_TYPE >(REF)
//
#endif //!defined(BOOST_MOVE_MSVC_AUTO_MOVE_RETURN_BUG) || defined(BOOST_MOVE_DOXYGEN_INVOKED)
#endif //BOOST_NO_CXX11_RVALUE_REFERENCES
#include <boost/move/detail/config_end.hpp>

View File

@@ -59,6 +59,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "move_test", "move_test.vcpr
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "doc_move_return", "doc_move_return.vcproj", "{7C1462C8-D532-4B8E-F2F6-E3A2A796D912}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
@@ -127,6 +131,10 @@ Global
{CD57C283-1862-42FE-BF87-B96D3A2A7912}.Debug.Build.0 = Debug|Win32
{CD57C283-1862-42FE-BF87-B96D3A2A7912}.Release.ActiveCfg = Release|Win32
{CD57C283-1862-42FE-BF87-B96D3A2A7912}.Release.Build.0 = Release|Win32
{7C1462C8-D532-4B8E-F2F6-E3A2A796D912}.Debug.ActiveCfg = Debug|Win32
{7C1462C8-D532-4B8E-F2F6-E3A2A796D912}.Debug.Build.0 = Debug|Win32
{7C1462C8-D532-4B8E-F2F6-E3A2A796D912}.Release.ActiveCfg = Release|Win32
{7C1462C8-D532-4B8E-F2F6-E3A2A796D912}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionItems) = postSolution
..\..\..\..\boost\move\algorithm.hpp = ..\..\..\..\boost\move\algorithm.hpp

View File

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="doc_move_return"
ProjectGUID="{7C1462C8-D532-4B8E-F2F6-E3A2A796D912}"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="../../Bin/Win32/Debug"
IntermediateDirectory="Debug/doc_move_return"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../../../.."
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;BOOST_DATE_TIME_NO_LIB"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
DisableLanguageExtensions="FALSE"
TreatWChar_tAsBuiltInType="TRUE"
ForceConformanceInForLoopScope="TRUE"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib"
OutputFile="$(OutDir)/doc_move_return_d.exe"
LinkIncremental="1"
AdditionalLibraryDirectories="../../../../stage/lib"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/doc_move_return.pdb"
SubSystem="1"
TargetMachine="1"
FixedBaseAddress="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="../../Bin/Win32/Release"
IntermediateDirectory="Release/doc_move_return"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../../../.."
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;BOOST_DATE_TIME_NO_LIB"
RuntimeLibrary="2"
TreatWChar_tAsBuiltInType="TRUE"
ForceConformanceInForLoopScope="FALSE"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="0"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="winmm.lib"
OutputFile="$(OutDir)/doc_move_return.exe"
LinkIncremental="1"
AdditionalLibraryDirectories="../../../../stage/lib"
GenerateDebugInformation="TRUE"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{475F3C87-6465-7BC5-05A6-2454C0A2A2CF}">
<File
RelativePath="..\..\example\doc_move_return.cpp">
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -11,6 +11,7 @@
#include <boost/move/detail/config_begin.hpp>
#include <boost/move/utility.hpp>
#include "../example/movable.hpp"
#include "../example/copymovable.hpp"
#include <boost/static_assert.hpp>
movable function(movable m)
@@ -57,11 +58,41 @@ void function_ref(const movable &)
void function_ref(BOOST_RV_REF(movable))
{}
struct copyable
{};
movable create_movable()
{ return movable(); }
template<class Type>
struct factory
{
Type operator()() const
{
Type t;
return BOOST_MOVE_RET(Type, t);
}
};
template<class Type>
struct factory<Type &>
{
static Type t;
Type &operator()() const
{
return BOOST_MOVE_RET(Type&, t);
}
};
template<class Type>
Type factory<Type&>::t;
template <class R, class F>
R factory_wrapper(F f)
{
// lock();
R r = f();
// unlock();
return BOOST_MOVE_RET(R, r);
}
int main()
{
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
@@ -78,34 +109,71 @@ int main()
movable m2(boost::move(m));
movable m3(function(movable(boost::move(m2))));
movable m4(function(boost::move(m3)));
(void)m;(void)m2;(void)m3;(void)m4;
}
{
movable m;
movable m2(boost::move(m));
movable m3(functionr(movable(boost::move(m2))));
movable m4(functionr(boost::move(m3)));
(void)m;(void)m2;(void)m3;(void)m4;
}
{
movable m;
movable m2(boost::move(m));
movable m3(function2(movable(boost::move(m2))));
movable m4(function2(boost::move(m3)));
(void)m;(void)m2;(void)m3;(void)m4;
}
{
movable m;
movable m2(boost::move(m));
movable m3(function2r(movable(boost::move(m2))));
movable m4(function2r(boost::move(m3)));
(void)m;(void)m2;(void)m3;(void)m4;
}
{
movable m;
movable m2(boost::move(m));
movable m3(move_return_function());
(void)m;(void)m2;(void)m3;
}
{
movable m;
movable m2(boost::move(m));
movable m3(move_return_function2());
(void)m;(void)m2;(void)m3;
}
{
//movable
movable m (factory_wrapper<movable>(factory<movable>()));
m = factory_wrapper<movable>(factory<movable>());
movable&mr(factory_wrapper<movable&>(factory<movable&>()));
movable&mr2 = factory_wrapper<movable&>(factory<movable&>());
(void)mr;
(void)mr2;
(void)m;
}
{
//copyable
copyable c (factory_wrapper<copyable>(factory<copyable>()));
c = factory_wrapper<copyable>(factory<copyable>());
copyable&cr(factory_wrapper<copyable&>(factory<copyable&>()));
copyable&cr2 = factory_wrapper<copyable&>(factory<copyable&>());
(void)cr;
(void)cr2;
(void)c;
}
{
//copy_movable
copy_movable c (factory_wrapper<copy_movable>(factory<copy_movable>()));
c = factory_wrapper<copy_movable>(factory<copy_movable>());
copy_movable&cr(factory_wrapper<copy_movable&>(factory<copy_movable&>()));
copy_movable&cr2 = factory_wrapper<copy_movable&>(factory<copy_movable&>());
(void)cr;
(void)cr2;
(void)c;
}
return 0;