<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0082)http://wiki.edg.com/twiki/pub/Wg21cologne/LibraryWorkingGroup/propagate_const.html -->
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" ""><HTML><HEAD><META 
content="IE=5.0000" http-equiv="X-UA-Compatible">

<META http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> 
<STYLE>
body 
{
  margin:2em;
  padding:0;
  max-width:60em;
}

blockquote
{
  margin-left:0em
}
</STYLE>

<META name="GENERATOR" content="MSHTML 11.00.9600.17631"></HEAD>     
<BODY>
<DIV><FONT size="-1"><BR><B>Doc number:</B> N4388 <BR><B>Revises:</B> N4372, 
N4209, N4057, N3973 <BR><B>Date:</B> 2015-02-25 <BR><B>Project:</B> Programming 
Language C++, Library Evolution Working Group <BR><B>Reply-to:</B> Jonathan Coe 
&lt;<A href="mailto:jbcoe@me.com" target="_blank">jbcoe@me.com</A>&gt;<BR>Robert 
Mill &lt;<A href="mailto:rob.mill.uk@gmail.com" 
target="_blank">rob.mill.uk@gmail.com</A>&gt;</FONT>     
<H1>A Proposal to Add a Const-Propagating Wrapper to the Standard Library</H1>
<H2>I. Introduction</H2>
<P>We propose the introduction of a <TT>propagate_const</TT> wrapper class that 
propagates <TT>const</TT>-ness to pointer-like member variables.</P>
<H2>II. Motivation</H2>
<P>The behaviour of <TT>const</TT> member functions on objects with pointer-like 
data members is seen to be surprising by many experienced C++ developers. A 
<TT>const</TT> member function can call non-<TT>const</TT> functions on 
pointer-like data members and will do so by default without use of 
<TT>const_cast</TT>.</P>
<P><B>Example:</B></P>
<BLOCKQUOTE><TT>
<PRE>struct A
{
  void bar() const 
  { 
    std::cout &lt;&lt; "bar (const)" &lt;&lt; std::endl; 
  }
  
  void bar() 
  { 
    std::cout &lt;&lt; "bar (non-const)" &lt;&lt; std::endl; 
  }
};

struct B
{
  B() : m_ptrA(std::make_unique&lt;A&gt;()) {} 
  
  void foo() const 
  { 
    std::cout &lt;&lt; "foo (const)" &lt;&lt; std::endl;
    m_ptrA-&gt;bar(); 
  }           
  
  void foo() 
  { 
    std::cout &lt;&lt; "foo (non-const)" &lt;&lt; std::endl;
    m_ptrA-&gt;bar(); 
  }

  std::unique_ptr&lt;A&gt; m_ptrA;
};

int main()
{    
  B b;
  b.foo();
  
  const B const_b;
  const_b.foo();
}

</PRE></TT></BLOCKQUOTE>
<P>Running this program gives the following output:</P>
<BLOCKQUOTE><TT>
<PRE>  foo (non-const)
  bar (non-const)
  foo (const)
  bar (non-const)
</PRE></TT></BLOCKQUOTE>
<P>The behaviour above can be amended by re-writing <TT>void B::foo() const</TT> 
using <TT>const_cast</TT> to explicitly call the <TT>const</TT> member function 
of <TT>A</TT>. Such a change is unnatural and not common practice. We propose 
the introduction of a wrapper class which can be used on pointer-like member 
data to ensure propagation of <TT>const</TT>-ness.</P>
<H4>Introducing <TT>propagate_const</TT></H4>
<P>The class <TT>propagate_const</TT> is designed to function as closely as 
possible to a traditional pointer or smart-pointer. Pointer-like member objects 
can be wrapped in a <TT>propagate_const</TT> object to ensure propagation of 
<TT>const</TT>-ness.</P>
<P>A <TT>const</TT>-propagating <TT>B</TT> would be written as</P>
<BLOCKQUOTE><TT>
<PRE>struct B
{
  B();              // unchanged
  
  void foo() const; // unchanged
  void foo();       // unchanged

  <FONT color="blue"><B>std::propagate_const&lt;</B></FONT>std::unique_ptr&lt;A&gt;<FONT color="blue"><B>&gt;</B></FONT> m_ptrA;
};
</PRE></TT></BLOCKQUOTE>
<P>With an amended <TT>B</TT>, running the program from the earlier example will 
give the following output:</P>
<BLOCKQUOTE><TT>
<PRE>  foo (non-const)
  bar (non-const)
  foo (const)
  bar (const)
</PRE></TT></BLOCKQUOTE>
<H3>The pimpl idiom with <TT>propagate_const</TT></H3>
<P>The pimpl (pointer-to-implementation) idiom pushes implementation details of 
a class into a separate object, a pointer to which is stored in the original 
class [2].</P>
<BLOCKQUOTE><TT>
<PRE>class C
{
  void foo() const;
  void foo();
    
  std::unique_ptr&lt;CImpl&gt; m_pimpl;
};

void C::foo() const 
{ 
  m_pimpl-&gt;foo(); 
}

void C::foo() 
{ 
  m_pimpl-&gt;foo(); 
}

</PRE></TT></BLOCKQUOTE>
<P>When using the pimpl idiom the compiler will not catch changes to member 
variables within <TT>const</TT> member functions. Member variables are kept in a 
separate object and the compiler only checks that the address of this object is 
unchanged. By introducing the pimpl idiom into a class to decouple interface and 
implementation, the author may have inadventantly lost compiler checks on 
<TT>const</TT>-correctness.</P>
<P>When the pimpl object is wrapped in <TT>propagate_const</TT>, <TT>const</TT> 
member functions will only be able to call <TT>const</TT> functions on the pimpl 
object and will be unable to modify (non-<TT>mutable</TT>) member variables of 
the pimpl object without explicit <TT>const_cast</TT>s: 
<TT>const</TT>-correctness is restored. The class above would be modified as 
follows:</P>
<BLOCKQUOTE><TT>
<PRE>class C
{
  void foo() const;  // unchanged
  void foo();        // unchanged
  
  <FONT color="blue"><B>std::propagate_const&lt;</B></FONT>std::unique_ptr&lt;CImpl&gt;<FONT color="blue"><B>&gt;</B></FONT> m_pimpl;
};</PRE></TT></BLOCKQUOTE>
<H3>Thread-safety and <TT>propagate_const</TT></H3>
<P>Herb Sutter introduced the appealing notion that <TT>const</TT> implies 
thread-safe [3]. Without <TT>propagate_const</TT>, changes outside a class with 
pointer-like members can render the <TT>const</TT> methods of that class 
non-thread-safe. This means that maintaining the rule 
<TT>const</TT>=&gt;thread-safe requires a global review of the code base.</P>
<P>With only the <TT>const</TT> version of <TT>foo()</TT> the code below is 
thread-safe. Introduction of a non-<TT>const</TT> (and non-thread-safe) 
<TT>foo()</TT> into <TT>D</TT> renders <TT>E</TT> non-thread-safe.</P>
<BLOCKQUOTE><TT>
<PRE>struct D
{
  int foo() const { /* thread-safe */ }
  int foo() { /* non-thread-safe */ }
};

struct E
{
  E(D&amp; pD) : m_pD{&amp;pD} {}

  void operator() () const
  {
    m_pD-&gt;foo();
  }

  D* m_pD;
};

int main()
{
  D d;
  const E e1(d);
  const E e2(d);

  std::thread t1(e1);
  std::thread t2(e2);
  t1.join();
  t2.join();
}
</PRE></TT></BLOCKQUOTE>
<P>One solution to the above is to forbid pointer-like member variables in 
classes if <TT>const</TT>=&gt;thread-safe. This is undesirably restrictive. If 
instead all pointer-like member variables are decorated with 
<TT>propagate_const</TT> then the compiler will catch violations of 
<TT>const</TT>-ness that could render code non-thread-safe.</P>
<BLOCKQUOTE><TT>
<PRE>struct E
{
  E(D&amp; pD);                 // unchanged

  void operator() () const; // unchanged

  <FONT color="blue"><B>std::propagate_const&lt;</B></FONT>D*<FONT color="blue"><B>&gt;</B></FONT> m_pD;
};</PRE></TT></BLOCKQUOTE>
<P>Introduction of <TT>propagate_const</TT> cannot automatically guarantee 
thread-safety but can allow <TT>const</TT>=&gt;thread-safe to be locally 
verified during code review.</P>
<H2>III. Impact On the Standard</H2>
<P>This proposal is a pure library extension. It does not require changes to any 
standard classes, functions or headers.</P>
<H2>IV. Design Decisions</H2>
<P>Given absolute freedom we would propose changing the <TT>const</TT> keyword 
to propagate <TT>const</TT>-ness. That would be impractical, however, as it 
would break existing code and change behaviour in potentially undesirable ways. 
A second approach would be the introduction of a new keyword to modify 
<TT>const</TT>, for instance, <TT>deep const</TT>, which enforces 
<TT>const</TT>-propagation. Although this change would maintain 
backward-compatibility, it would require enhancements to the C++ compiler.</P>
<P>We suggest that the standard library supply a class that wraps member data 
where <TT>const</TT>-propagating behaviour is required. The 
<TT>propagate_const</TT> wrapper can be used much like the <TT>const</TT> 
keyword and will cause compilation failure wherever <TT>const</TT>-ness is 
violated. <TT>const</TT>-propagation can be introduced into existing code by 
decorating pointer-like members of a class with <TT>propagate_const</TT>.</P>
<P>The change required to introduce <TT>const</TT>-propagation to a class is 
simple and local enough to be enforced during code review and taught to C++ 
developers in the same way as smart-pointers are taught to ensure exception 
safety.</P>
<P>It is intended that <TT>propagate_const</TT> contain no member data besides 
the wrapped pointer. Inlining of function calls by the compiler will ensure that 
using <TT>propagate_const</TT> incurs no run-time cost.</P>
<H3>Encapsulation vs inheritance</H3>
<P>Inheritance from the wrapped pointer-like object (where it is a class type) 
was considered but ruled out. The purpose of this wrapper is to help the author 
ensure <TT>const</TT>-propagation; if <TT>propagate_const&lt;T&gt;</TT> were to 
inherit from <TT>T</TT>, then it would allow potentially non-<TT>const</TT> 
member functions of <TT>T</TT> to be called in a <TT>const</TT> context.</P>
<H3>Construction and assignment</H3>
<P> A <TT>propagate_const&lt;T&gt;</TT> should be move-constructable and 
move-assignable from a <TT>U</TT> or a <TT>propagate_const&lt;U&gt;</TT> where 
<TT>U</TT> is any type that <TT>T</TT> can be constructed or assigned from. 
 There should be no additional cost of construction for a 
<TT>propagate_const&lt;T&gt;</TT> beyond that for construction of a <TT>T</TT>. 
 The wrapped <TT>T</TT> should not be value-initialized as this would incur a 
cost for object pointer types.  If value-initialization is desirable then it can 
be accomplished with another wrapper class like 
<TT>boost::value_initialized</TT> [4].</P>
<P>The constructors should be revised to use <I><TT>EXPLICIT</TT></I> from N4387 
once the Library Fundamentals TS references C++17.</P>
<H3>Non-copyable</H3>
<P> A <TT>propagate_const&lt;T&gt;</TT> cannot be copied or assigned from a 
<TT>const propagate_const&lt;T&gt;&amp;</TT>.  This is to prevent unintentional 
removal of <TT>const-safety</TT> through copying. For instance: </P>
<BLOCKQUOTE><TT>
<PRE>struct G;
struct F
{
  void const_method() const; // calls only const methods of m_g;
  
  void non_const_method(); // calls methods of m_g
  
  propagate_const&lt;G*&gt; m_g;
};

void some_function(const F&amp; f) // const-ref passed in, no resources should be accessed in a non-const manner
{
  F copy_f = f;
  f.non_const_method();
}
</PRE></TT></BLOCKQUOTE>
<P>As <TT>propagate_const</TT> cannot be copied, the default copy and assignment 
operators will not be generated for <TT>F</TT>. This will prevent the code above 
from compiling. </P>
<H3>Pointer-like functions</H3>
<P><TT>operator*</TT> and <TT>operator-&gt;</TT> are defined to preserve 
<TT>const</TT>-propagation.  When a <TT>const</TT> 
<TT>propagate_const&lt;T&gt;</TT> is used only <TT>const</TT> member functions 
 of <TT>T</TT> can be used without explicit casts.</P>
<P>The wrapped pointer in <TT>propagate_const&lt;T&gt;</TT> is only required to 
be non-null, not dereferenceable, for <TT>operator*</TT> and 
<TT>operator-&gt;</TT> as past-the-end-of-array pointers are not considered. 
Pointer arithemtic is not supported, this is  consistent with existing practice 
for standard library smart pointers.</P>
<H3><TT>get</TT></H3>
<P>The <TT>get</TT> function returns the address of the object pointed to by the 
wrapped pointer. <TT>get</TT> is intended to be used to ensure 
<TT>const</TT>-propagation is preserved when using interfaces which require 
C-style pointers</P>
<H3><TT>operator value*</TT></H3>
<P>When <TT>T</TT> is an object pointer type <TT>operator value*</TT> exists and 
allows implicit conversion to a pointer. This avoids using <TT>get</TT> to 
access the pointer in contexts where it was unnecesary before addition of the 
<TT>propagate_const</TT> wrapper.</P>
<H3>Equality, inequality and comparison</H3>
<P>Free-standing equality, inequality and comparison operators are provided so 
that a <TT>propagate_const&lt;T&gt;</TT> can be used in any equality, inequality 
or comparison where a <TT>T</TT> could be used. <TT>const</TT>-propagation 
should not alter the result of any equality, inequality or comparison 
operation.</P>
<H3><TT>swap</TT></H3>
<P><TT>swap</TT> will swap the underlying pointers.</P>
<H3><TT>get_underlying</TT></H3>
<P><TT>get_underlying</TT> is a free-standing function which allows the 
underlying pointer to be accessed. The use of this function allows 
<TT>const</TT>-propagation to be dropped and is therefore discouraged. The 
function is named such that it will be easy to find in code review.</P>
<H3><TT>hash</TT></H3>
<P>The <TT>hash</TT> struct is specialized so that inclusion of 
<TT>propagate_const</TT> does not alter the result of hash evaluation.</P>
<H2>V. Technical Specification</H2>
<H2>X.Y&nbsp;&nbsp;&nbsp;Class template <TT>propagate_const</TT> 
[propagate_const]</H2>
<H3>X.Y.1&nbsp;&nbsp;Class template <TT>propagate_const</TT> general 
[propagate_const.general]</H3>
<P><TT>propagate_const</TT> is a wrapper around a pointer-like object type 
<TT>T</TT> which treats the wrapped pointer as a pointer to <TT>const</TT> when
 the wrapper is accessed through a <TT>const</TT> access path.  
<H3>X.Y.2&nbsp;&nbsp;Header <TT>&lt;propagate_const&gt;</TT> synopsis 
[propagate_const.synopsis]</H3>
<BLOCKQUOTE style="margin-left: 0em;"><TT>
<PRE>namespace std {
namespace experimental {
inline namespace fundamentals_v2 {
  template &lt;class T&gt; class propagate_const { 
  public:                                                                                          
    typedef remove_reference_t&lt;decltype(*declval&lt;T&amp;&gt;())&gt; element_type;

    //<I> [propagate_const.ctor], constructors</I>
    constexpr propagate_const() = default;
    propagate_const(const propagate_const&amp; p) = delete;
    constexpr propagate_const(propagate_const&amp;&amp; p) = default;
    template &lt;class U&gt; 
      <I>see below</I> constexpr propagate_const(propagate_const&lt;U&gt;&amp;&amp; pu);
    template &lt;class U&gt; 
      <I>see below</I> constexpr propagate_const(U&amp;&amp; u); 
    
    //<I> [propagate_const.assignment], assignment</I>
    propagate_const&amp; operator=(const propagate_const&amp; p) = delete; 
    constexpr propagate_const&amp; operator=(propagate_const&amp;&amp; p) = default; 
    template &lt;class U&gt; 
      constexpr propagate_const&amp; operator=(propagate_const&lt;U&gt;&amp;&amp; pu);
    template &lt;class U&gt; 
      constexpr propagate_const&amp; operator=(U&amp;&amp; u); 
    
    //<I> [propagate_const.const_observers], const observers</I>
    explicit constexpr operator bool() const;
    constexpr const element_type* operator-&gt;() const;
    constexpr operator const element_type*() const; //<I>Not always defined</I>
    constexpr const element_type&amp; operator*() const;
    constexpr const element_type* get() const;
    
    //<I> [propagate_const.non_const_observers], non-const observers</I>
    constexpr element_type* operator-&gt;();
    constexpr operator element_type*(); //<I>Not always defined</I> 
    constexpr element_type&amp; operator*();
    constexpr element_type* get();

    //<I> [propagate_const.modifiers], modifiers</I>
    constexpr void swap(propagate_const&amp; pt) noexcept(<I>see below</I>);
 
  private:
    T t_; //<I>exposition only</I>
  };
  
  //<I> [propagate_const.relational], relational operators</I>
  template &lt;class T&gt;
    constexpr bool operator==(const propagate_const&lt;T&gt;&amp; pt, nullptr_t);
  template &lt;class T&gt;
    constexpr bool operator==(nullptr_t, const propagate_const&lt;T&gt;&amp; pu);
  
  template &lt;class T&gt;
    constexpr bool operator!=(const propagate_const&lt;T&gt;&amp; pt, nullptr_t);
  template &lt;class T&gt;
    constexpr bool operator!=(nullptr_t, const propagate_const&lt;T&gt;&amp; pu);
  
  template &lt;class T, class U&gt;
    constexpr bool operator==(const propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator!=(const propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator&lt;(const propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator&gt;(const propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator&lt;=(const propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator&gt;=(const propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; pu);

  template &lt;class T, class U&gt;
    constexpr bool operator==(const propagate_const&lt;T&gt;&amp; pt, const U&amp; u);
  template &lt;class T, class U&gt;
    constexpr bool operator!=(const propagate_const&lt;T&gt;&amp; pt, const U&amp; u);
  template &lt;class T, class U&gt;
    constexpr bool operator&lt;(const propagate_const&lt;T&gt;&amp; pt, const U&amp; u);
  template &lt;class T, class U&gt;
    constexpr bool operator&gt;(const propagate_const&lt;T&gt;&amp; pt, const U&amp; u);
  template &lt;class T, class U&gt;
    constexpr bool operator&lt;=(const propagate_const&lt;T&gt;&amp; pt, const U&amp; u);
  template &lt;class T, class U&gt;
    constexpr bool operator&gt;=(const propagate_const&lt;T&gt;&amp; pt, const U&amp; u);
  
  template &lt;class T, class U&gt;
    constexpr bool operator==(const T&amp; t, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator!=(const T&amp; t, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator&lt;(const T&amp; t, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator&gt;(const T&amp; t, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator&lt;=(const T&amp; t, const propagate_const&lt;U&gt;&amp; pu);
  template &lt;class T, class U&gt;
    constexpr bool operator&gt;=(const T&amp; t, const propagate_const&lt;U&gt;&amp; pu);
  
  //<I> [propagate_const.algorithms], specialized algorithms</I>
  template &lt;class T&gt;
    constexpr void swap(propagate_const&lt;T&gt;&amp; pt, propagate_const&lt;T&gt;&amp; pt2) noexcept(<I>see below</I>);

  //<I> [propagate_const.underlying], underlying pointer access</I>
  template &lt;class T&gt;
    constexpr const T&amp; get_underlying(const propagate_const&lt;T&gt;&amp; pt) noexcept;
  template &lt;class T&gt;
    constexpr T&amp; get_underlying(propagate_const&lt;T&gt;&amp; pt) noexcept;
} // <I> end namespace fundamentals_v2</I>   
} // <I> end namespace experimental</I>

  //<I> [propagate_const.hash], hash support</I>
  template &lt;class T&gt; struct hash;
  template &lt;class T&gt;
    struct hash&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;;
  
  //<I> [propagate_const.comparison_function_objects], comparison function objects</I>
  template &lt;class T&gt; struct equal_to;
  template &lt;class T&gt;
    struct equal_to&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;;
  template &lt;class T&gt; struct not_equal_to;
  template &lt;class T&gt;
    struct not_equal_to&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;;
  template &lt;class T&gt; struct less;
  template &lt;class T&gt;
    struct less&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;;
  template &lt;class T&gt; struct greater;
  template &lt;class T&gt;
    struct greater&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;;
  template &lt;class T&gt; struct less_equal;
  template &lt;class T&gt;
    struct less_equal&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;;
  template &lt;class T&gt; struct greater_equal;
  template &lt;class T&gt;
    struct greater_equal&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;;
} // <I>end namespace std</I></PRE></TT></BLOCKQUOTE>
<H3>X.Y.3&nbsp;&nbsp;<TT>propagate_const</TT> requirements on <TT>T</TT> 
[propagate_const.requirements]</H3>
<P><TT>T</TT> shall be an object pointer type or a class type for which 
<TT>decltype(*declval&lt;T&amp;&gt;())</TT> is an lvalue reference; otherwise 
the program is ill-formed. </P>
<P>If <TT>T</TT> is an array type, reference type, pointer to function type or 
pointer to (possibly cv-qualified) <TT>void</TT>, then the program is 
ill-formed. 
<P>
<P>[<I>Note</I>: <TT>propagate_const&lt;const int*&gt;</TT> is well-formed  
<I>end note</I>] </P>
<P>
<H4>X.Y.3.1&nbsp;&nbsp;<TT>propagate_const</TT> requirements on class type 
<TT>T</TT> [propagate_const.class_type_requirements]</H4>If <TT>T</TT> is class 
type then it shall satisfy the following requirements. In this sub-clause 
<TT>t</TT> denotes a non-<TT>const</TT> lvalue of type <TT>T</TT>, <TT>ct</TT> 
is a <TT>const T&amp;</TT> bound to <TT>t</TT>,  <TT>element_type</TT> denotes 
an object type. 
<P>
<P><TT>T</TT> and <TT>const T</TT> shall be contextually convertible to 
<TT>bool</TT>. </P>
<P>If <TT>T</TT> is implicitly convertible to <TT>element_type*</TT>,  
<TT>(element_type*)t == t.get()</TT> shall be <TT>true</TT>. 
<P>
<P>If <TT>const T</TT> is implicitly convertible to <TT>const 
element_type*</TT>, <TT>(const element_type*)ct == ct.get()</TT> shall be 
<TT>true</TT>. </P>
<BLOCKQUOTE>
  <TABLE border="1">
    <CAPTION>Table XX  Requirements on class types <TT>T</TT></CAPTION>
    <TBODY>
    <TR>
      <TH>Expression</TH>
      <TH>Return type</TH>
      <TH>Pre-conditions</TH>
      <TH>Operational<BR>semantics</TH></TR>
    <TR>
      <TD><TT>t.get()</TT>       </TD>
      <TD><TT>element_type*</TT>       </TD>
      <TD><TT></TT>       </TD>
      <TD><TT></TT>       </TD></TR>
    <TR>
      <TD><TT>ct.get()</TT>       </TD>
      <TD><TT>const element_type*</TT> or <TT>element_type*</TT>       </TD>
      <TD><TT></TT>       </TD>
      <TD><TT>t.get() == ct.get()</TT>.       </TD></TR>
    <TR>
      <TD><TT>*t</TT>       </TD>
      <TD><TT>element_type&amp;</TT>       </TD>
      <TD><TT>t.get() != nullptr</TT>       </TD>
      <TD><TT>*t</TT> refers to the same object as <TT>*(t.get())</TT>       
      </TD></TR>
    <TR>
      <TD><TT>*ct</TT>       </TD>
      <TD><TT>const element_type&amp;</TT> or <TT>element_type&amp;</TT>       
      </TD>
      <TD><TT>ct.get() != nullptr</TT>       </TD>
      <TD><TT>*ct</TT> refers to the same object as <TT>*(ct.get())</TT>       
      </TD></TR>
    <TR>
      <TD><TT>t.operator-&gt;()</TT>       </TD>
      <TD><TT>element_type*</TT>       </TD>
      <TD><TT>t.get() != nullptr</TT>       </TD>
      <TD><TT>t.operator-&gt;() == t.get()</TT>       </TD></TR>
    <TR>
      <TD><TT>ct.operator-&gt;()</TT>       </TD>
      <TD><TT>const element_type*</TT> or <TT>element_type*</TT>       </TD>
      <TD><TT>ct.get() != nullptr</TT>       </TD>
      <TD><TT>ct.operator-&gt;() == ct.get()</TT>       </TD></TR>
    <TR>
      <TD><TT>(bool)t</TT>       </TD>
      <TD><TT>bool</TT>       </TD>
      <TD><TT></TT>       </TD>
      <TD><TT>(bool)t</TT> is equivalent to <TT>t.get() != nullptr</TT>       
      </TD></TR>
    <TR>
      <TD><TT>(bool)ct</TT>       </TD>
      <TD><TT>bool</TT>       </TD>
      <TD><TT></TT>       </TD>
      <TD><TT>(bool)ct</TT> is equivalent to <TT>ct.get() != nullptr</TT>      
       </TD></TR></TBODY></TABLE></BLOCKQUOTE>
<H3>X.Y.4&nbsp;&nbsp;<TT>propagate_const</TT> constructors 
[propagate_const.ctor]</H3>
<P> [<I>Note</I>: The following constructors are conditionally specified as 
<TT>explicit</TT>.  This is typically implemented by declaring two such 
constructors, of which at most one  participates in overload resolution.  
<I>end note</I>] </P>
<DIV style="margin-left: 20px;"><B><TT>template &lt;class U&gt; <I>see below</I> 
constexpr propagate_const(propagate_const&lt;U&gt;&amp;&amp; pu)</TT><BR></B> 
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Remarks:</I> This constructor shall not participate in overload 
      resolution unless        <TT>is_constructible_v&lt;T, 
      U&amp;&amp;&gt;</TT>. The constructor is specified as <TT>explicit</TT> if 
      and only if        <TT>!is_convertible_v&lt;U&amp;&amp;, T&gt;.</TT></TD></TR>
  <TR>
    <TD><SUP>2</SUP></TD>
    <TD><I>Effects:</I> Initializes <TT>t_</TT> as if 
      direct-non-list-initializing an object of type        <TT>T</TT> with the 
      expression <TT>std::move(pu.t_)</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>template &lt;class U&gt; <I>see below</I> constexpr 
propagate_const(U&amp;&amp; u)</TT><BR></B> 
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>3</SUP></TD>
    <TD><I>Remarks:</I> This constructor shall not participate in overload 
      resolution unless        <TT>is_constructible_v&lt;T, U&amp;&amp;&gt;</TT> 
      and <TT>decay_t&lt;U&gt;</TT> is not a specialization of        
      <TT>propagate_const</TT>. The constructor is specified as 
      <TT>explicit</TT> if and only if        
      <TT>!is_convertible_v&lt;U&amp;&amp;, T&gt;.</TT></TD></TR>
  <TR>
    <TD><SUP>4</SUP></TD>
    <TD><I>Effects:</I> Initializes <TT>t_</TT> as if 
      direct-non-list-initializing an object of type        <TT>T</TT> with the 
      expression 
<TT>std::forward&lt;U&gt;(u)</TT>.</TD></TR></TBODY></TABLE></P></DIV>
<H3>X.Y.5&nbsp;&nbsp;<TT>propagate_const</TT> assignment 
[propagate_const.assignment]</H3>
<DIV style="margin-left: 20px;"><B><TT>template &lt;class U&gt; constexpr 
propagate_const operator=(propagate_const&lt;U&gt;&amp;&amp; pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Remarks:</I> This function shall not participate in overload 
      resolution unless <TT>U</TT> is implicitly convertible to 
<TT>T</TT>.</TD></TR>
  <TR>
    <TD><SUP>2</SUP></TD>
    <TD><I>Effects:</I> <TT>t_ = std::move(pu.t_)</TT>.</TD></TR>
  <TR>
    <TD><SUP>3</SUP></TD>
    <TD><I>Returns:</I> <TT>*this</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>template &lt;class U&gt; constexpr propagate_const 
operator=(U&amp;&amp; u)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>4</SUP></TD>
    <TD><I>Remarks:</I> This function shall not participate in overload 
      resolution unless <TT>U</TT> is implicitly convertible to <TT>T</TT> and 
      <TT>decay_t&lt;U&gt;</TT> is not a specialization of 
      <TT>propagate_const</TT>.</TD></TR>
  <TR>
    <TD><SUP>5</SUP></TD>
    <TD><I>Effects:</I> <TT>t_ = std::forward&lt;U&gt;(u)</TT>.</TD></TR>
  <TR>
    <TD><SUP>6</SUP></TD>
    <TD><I>Returns:</I> <TT>*this</TT>.</TD></TR></TBODY></TABLE></P></DIV>
<H3>X.Y.6&nbsp;&nbsp;<TT>propagate_const</TT> const observers 
[propagate_const.const_observers]</H3>
<DIV style="margin-left: 20px;"><B><TT>explicit constexpr operator bool() 
const</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Returns:</I><TT>(bool)t_</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>constexpr const element_type* operator-&gt;() const</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>2</SUP></TD>
    <TD><I>Requires:</I> <TT>get() != nullptr</TT>.</TD></TR>
  <TR>
    <TD><SUP>3</SUP></TD>
    <TD><I>Returns:</I> <TT>get()</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>constexpr operator const element_type*() const</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>4</SUP></TD>
    <TD><I>Returns:</I> <TT>get()</TT>.</TD></TR>
  <TR>
    <TD><SUP>5</SUP></TD>
    <TD><I>Remarks:</I> This function shall not participate in overload 
      resolution unless <TT>T</TT> is an object pointer type or has an implicit 
      conversion to <TT>const element_type*</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>constexpr const element_type&amp; operator*() const</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>6</SUP></TD>
    <TD><I>Requires:</I> <TT>get() != nullptr</TT>.</TD></TR>
  <TR>
    <TD><SUP>7</SUP></TD>
    <TD><I>Returns:</I> <TT>*get()</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>constexpr const element_type* get() const</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>8</SUP></TD>
    <TD><I>Returns:</I> <TT>t_</TT> if <TT>T</TT> is an object pointer type, 
      otherwise <TT>t_.get()</TT>.</TD></TR></TBODY></TABLE></P></DIV>
<H3>X.Y.7&nbsp;&nbsp;<TT>propagate_const</TT> non-const observers 
[propagate_const.non_const_observers]</H3>
<DIV style="margin-left: 20px;"><B><TT>constexpr element_type* 
operator-&gt;()</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Requires:</I> <TT>get() != nullptr</TT>.</TD></TR>
  <TR>
    <TD><SUP>2</SUP></TD>
    <TD><I>Returns:</I> <TT>get()</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>constexpr operator element_type*()</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>3</SUP></TD>
    <TD><I>Returns:</I> <TT>get()</TT>.</TD></TR>
  <TR>
    <TD><SUP>4</SUP></TD>
    <TD><I>Remarks:</I> This function shall not participate in overload 
      resolution unless <TT>T</TT> is an object pointer type or has an implicit 
      conversion to <TT>element_type*</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>constexpr element_type&amp; operator*()</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>5</SUP></TD>
    <TD><I>Requires:</I> <TT>get() != nullptr</TT>.</TD></TR>
  <TR>
    <TD><SUP>6</SUP></TD>
    <TD><I>Returns:</I> <TT>*get()</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>constexpr element_type* get()</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>7</SUP></TD>
    <TD><I>Returns:</I> <TT>t_</TT> if <TT>T</TT> is an object pointer type, 
      otherwise <TT>t_.get()</TT>.</TD></TR></TBODY></TABLE></P></DIV>
<H3>X.Y.8&nbsp;&nbsp;<TT>propagate_const</TT> modifiers 
[propagate_const.modifiers]</H3>
<DIV style="margin-left: 20px;"><B><TT>constexpr void swap(propagate_const&amp; 
pt) noexcept(<I>see below</I>)</TT></B><BR>
<P>The constant-expression in the exception-specification is 
<TT>noexcept(swap(t_, pt.t_))</TT>.</P>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Effects:</I> <TT>swap(t_, pt.t_)</TT>.</TD></TR></TBODY></TABLE></DIV>
<H3>X.Y.9&nbsp;&nbsp;<TT>propagate_const</TT> relational operators 
[propagate_const.relational]</H3>
<DIV style="margin-left: 20px;"><B><TT>template &lt;class T&gt;      constexpr 
bool operator==(const propagate_const&lt;T&gt;&amp; pt, nullptr_t)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ == 
nullptr</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T&gt;      
constexpr bool operator==(nullptr_t, const propagate_const&lt;U&gt;&amp; 
pt)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>2</SUP></TD>
    <TD><I>Returns:</I> <TT>nullptr == 
pt.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T&gt;      
constexpr bool operator!=(const propagate_const&lt;T&gt;&amp; pt, 
nullptr_t)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>3</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ != 
nullptr</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T&gt;      
constexpr bool operator!=(nullptr_t, const propagate_const&lt;T&gt;&amp; 
pt)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>4</SUP></TD>
    <TD><I>Returns:</I> <TT>nullptr != pt.t_</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>template &lt;class T, class U&gt;<BR>      &nbsp;&nbsp;constexpr bool 
operator==(const propagate_const&lt;T&gt;&amp; pt, const 
propagate_const&lt;U&gt;&amp; pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>5</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ == 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class 
U&gt;<BR>     &nbsp;&nbsp;constexpr bool operator!=(const 
propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; 
pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>6</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ != 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class 
U&gt;<BR>       &nbsp;&nbsp;constexpr bool operator&lt;(const 
propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; 
pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>7</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ &lt; 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class 
U&gt;<BR>      &nbsp;&nbsp;constexpr bool operator&gt;(const 
propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; 
pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>8</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ &gt; 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class 
U&gt;<BR>      &nbsp;&nbsp;constexpr bool operator&lt;=(const 
propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; 
pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>9</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ &lt;= 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class 
U&gt;<BR>      &nbsp;&nbsp;constexpr bool operator&gt;=(const 
propagate_const&lt;T&gt;&amp; pt, const propagate_const&lt;U&gt;&amp; 
pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>10</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ &gt;= pu.t_</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>template &lt;class T, class U&gt;       constexpr bool 
operator==(const propagate_const&lt;T&gt;&amp; pt, const U&amp; u)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>11</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ == 
u</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;       
constexpr bool operator!=(const propagate_const&lt;T&gt;&amp; pt, const U&amp; 
u)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>12</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ != 
u</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;       
constexpr bool operator&lt;(const propagate_const&lt;T&gt;&amp; pt, const U&amp; 
u)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>13</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ &lt; 
u</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;       
constexpr bool operator&gt;(const propagate_const&lt;T&gt;&amp; pt, const U&amp; 
u)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>14</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ &gt; 
u</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;       
constexpr bool operator&lt;=(const propagate_const&lt;T&gt;&amp; pt, const 
U&amp; u)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>15</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ &lt;= 
u</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;       
constexpr bool operator&gt;=(const propagate_const&lt;T&gt;&amp; pt, const 
U&amp; u)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>16</SUP></TD>
    <TD><I>Returns:</I> <TT>pt.t_ &gt;= u</TT>.</TD></TR></TBODY></TABLE>
<P><B><TT>template &lt;class T, class U&gt;       constexpr bool 
operator==(const T&amp; t, const propagate_const&lt;U&gt;&amp; pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>17</SUP></TD>
    <TD><I>Returns:</I> <TT>t == 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;    
   constexpr bool operator!=(const T&amp; t, const propagate_const&lt;U&gt;&amp; 
pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>18</SUP></TD>
    <TD><I>Returns:</I> <TT>t != 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;    
   constexpr bool operator&lt;(const T&amp; t, const 
propagate_const&lt;U&gt;&amp; pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>19</SUP></TD>
    <TD><I>Returns:</I> <TT>t &lt; 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;    
   constexpr bool operator&gt;(const T&amp; t, const 
propagate_const&lt;U&gt;&amp; pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>20</SUP></TD>
    <TD><I>Returns:</I> <TT>t &gt; 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;    
   constexpr bool operator&lt;=(const T&amp; t, const 
propagate_const&lt;U&gt;&amp; pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>21</SUP></TD>
    <TD><I>Returns:</I> <TT>t &lt;= 
pu.t_</TT>.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T, class U&gt;    
   constexpr bool operator&gt;=(const T&amp; t, const 
propagate_const&lt;U&gt;&amp; pu)</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>22</SUP></TD>
    <TD><I>Returns:</I> <TT>t &gt;= pu.t_</TT>.</TD></TR></TBODY></TABLE></P></DIV>
<H3>X.Y.10&nbsp;&nbsp;<TT>propagate_const</TT> specialized algorithms 
[propagate_const.algorithms]</H3>
<DIV style="margin-left: 20px;"><B><TT>template &lt;class T&gt; constexpr void 
swap(propagate_const&lt;T&gt;&amp; pt1, propagate_const&lt;T&gt;&amp; pt2) 
noexcept(<I>see below</I>)</TT></B><BR>
<P>The constant-expression in the exception-specification is 
<TT>noexcept(swap(pt1.t_, pt2.t_))</TT>.</P>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Effects:</I> <TT>swap(pt1.t_, 
pt2.t_)</TT>.</TD></TR></TBODY></TABLE></DIV>
<H3>X.Y.11&nbsp;&nbsp;<TT>propagate_const</TT> underlying pointer access 
[propagate_const.underlying]</H3>
<P style="margin-left: 20px;">Access to the underlying object pointer type is 
through free functions rather than member functions.  These functions are 
intended to resemble cast operations to encourage caution when using them.</P>
<DIV style="margin-left: 20px;"><B><TT>template &lt;class T&gt; constexpr const 
T&amp; get_underlying(const propagate_const&lt;T&gt;&amp; pt) 
noexcept</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Returns:</I> a reference to the underlying object pointer 
  type.</TD></TR></TBODY></TABLE><B><TT>template &lt;class T&gt; constexpr T&amp; 
get_underlying(propagate_const&lt;T&gt;&amp; pt) noexcept</TT></B><BR>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>2</SUP></TD>
    <TD><I>Returns:</I> a reference to the underlying object pointer 
  type.</TD></TR></TBODY></TABLE></DIV>
<H3>X.Y.12&nbsp;&nbsp;<TT>propagate_const</TT> hash support 
[propagate_const.hash]</H3>
<DIV style="margin-left: 20px;"><B><TT>template &lt;class T&gt; struct 
hash&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;</TT></B><BR>
<DIV>
<P style="margin-left: 20px;">For an object <TT>p</TT> of type 
<TT>propagate_const&lt;T&gt;</TT>, 
<TT>hash&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;()(p)</TT> 
   shall evaluate to the same value as <TT>hash&lt;T&gt;()(p.t_)</TT>.</P>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Requires:</I> The specialization <TT>hash&lt;T&gt;       </TT> 
      shall be well-formed and well-defined, and shall meet the requirements of 
      class template hash.</TD></TR></TBODY></TABLE></DIV></DIV>
<H3>X.Y.13&nbsp;&nbsp;<TT>propagate_const</TT> comparison function objects 
[propagate_const.comparison_function_objects]</H3>
<DIV style="margin-left: 20px;"><B><TT>template &lt;class T&gt; struct 
equal_to&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;</TT></B> 
  
<DIV>
<P style="margin-left: 20px;">For objects <TT>p, q</TT> of type 
<TT>propagate_const&lt;T&gt;</TT>, 
<TT>equal_to&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;()(p, 
q)</TT>    shall evaluate to the same value as <TT>equal_to&lt;T&gt;()(p.t_, 
q.t_)</TT>.</P>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Requires:</I> The specialization <TT>equal_to&lt;T&gt;       </TT> 
      shall be well-formed and 
well-defined.</TD></TR></TBODY></TABLE></DIV><B><TT>template &lt;class T&gt; 
struct 
not_equal_to&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;</TT></B> 
  
<DIV>
<P style="margin-left: 20px;">For objects <TT>p, q</TT> of type 
<TT>propagate_const&lt;T&gt;</TT>, 
<TT>not_equal_to&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;()(p, 
q)</TT>    shall evaluate to the same value as <TT>not_equal_to&lt;T&gt;()(p.t_, 
q.t_)</TT>.</P>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Requires:</I> The specialization <TT>not_equal_to&lt;T&gt;       
      </TT> shall be well-formed and 
well-defined.</TD></TR></TBODY></TABLE></DIV><B><TT>template &lt;class T&gt; 
struct 
less&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;</TT></B>   
<DIV>
<P style="margin-left: 20px;">For objects <TT>p, q</TT> of type 
<TT>propagate_const&lt;T&gt;</TT>, 
<TT>less&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;()(p, 
q)</TT>    shall evaluate to the same value as <TT>less&lt;T&gt;()(p.t_, 
q.t_)</TT>.</P>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Requires:</I> The specialization <TT>less&lt;T&gt;       </TT> 
      shall be well-formed and 
well-defined.</TD></TR></TBODY></TABLE></DIV><B><TT>template &lt;class T&gt; 
struct 
greater&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;</TT></B>  
 
<DIV>
<P style="margin-left: 20px;">For objects <TT>p, q</TT> of type 
<TT>propagate_const&lt;T&gt;</TT>, 
<TT>greater&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;()(p, 
q)</TT>    shall evaluate to the same value as <TT>greater&lt;T&gt;()(p.t_, 
q.t_)</TT>.</P>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Requires:</I> The specialization <TT>greater&lt;T&gt;       </TT> 
      shall be well-formed and 
well-defined.</TD></TR></TBODY></TABLE></DIV><B><TT>template &lt;class T&gt; 
struct 
less_equal&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;</TT></B> 
  
<DIV>
<P style="margin-left: 20px;">For objects <TT>p, q</TT> of type 
<TT>propagate_const&lt;T&gt;</TT>, 
<TT>less_equal&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;()(p, 
q)</TT>    shall evaluate to the same value as <TT>less_equal&lt;T&gt;()(p.t_, 
q.t_)</TT>.</P>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Requires:</I> The specialization <TT>less_equal&lt;T&gt;       
      </TT> shall be well-formed and 
well-defined.</TD></TR></TBODY></TABLE></DIV><B><TT>template &lt;class T&gt; 
struct 
greater_equal&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;</TT></B> 
  
<DIV>
<P style="margin-left: 20px;">For objects <TT>p, q</TT> of type 
<TT>propagate_const&lt;T&gt;</TT>, 
<TT>greater_equal&lt;experimental::fundamentals_v2::propagate_const&lt;T&gt;&gt;()(p, 
q)</TT>    shall evaluate to the same value as 
<TT>greater_equal&lt;T&gt;()(p.t_, q.t_)</TT>.</P>
<TABLE cellpadding="6">
  <TBODY>
  <TR>
    <TD><SUP>1</SUP></TD>
    <TD><I>Requires:</I> The specialization <TT>greater_equal&lt;T&gt;       
      </TT> shall be well-formed and 
well-defined.</TD></TR></TBODY></TABLE></DIV></DIV>
<H2>VI. Acknowledgements</H2>
<P>Thanks to Walter Brown, Kevin Channon, Daniel Krugler, Stephan T. Lavavej, 
Nick Maclaren, Roger Orr, Geoffrey Romer, Ville Voutilainen, Jonathan Wakely, 
David Ward, the staff of the Creative Assembly and others for helpful 
discussion.</P>
<H2>VII. Revisions</H2>
<P>This paper revises N4372</P>
<UL>
  <LI>Added comparison function object specializations for 
  <TT>propagate_const</TT>.</LI>
  <LI>Added stricter restrictions on template type <TT>T</TT>.</LI>
  <LI>Added defaulted r-value constructor and assignment.</LI>
  <LI>Simplified definitions to use the exposition-only member <TT>t_</TT>.</LI>
  <LI>Minor cosmetic changes.</LI></UL>
<P>N4372 revises N4209</P>
<UL>
  <LI>Default constructor is now defaulted.</LI>
  <LI>Added <TT>constexpr</TT>.</LI>
  <LI>Added <TT>noexcept</TT>.</LI>
  <LI>Altered relational operators, assignement and swap to use 
<TT>t_</TT>.</LI>
  <LI>Copy and assignment from const reference are now deleted.</LI></UL>
<P>N4209 revises N4057</P>
<UL>
  <LI>Extended comparison operators to avoid reliance on implicit 
  conversions.</LI>
  <LI>Minor cosmetic changes.</LI></UL>
<P>N4057 revises N3973</P>
<UL>
  <LI>Renamed class from <TT>logical_const</TT> to <TT>propagate_const</TT> as 
  the former is used with different meaning in the D community.</LI>
  <LI>Add <TT>enable_if</TT> to reference implementation to prevent some 
  functions entering into an overload set.</LI></UL>
<H2>VIII. References</H2>
<UL>
  <LI>[1] Bjarne Stroustrup, The C++ Programming Language, 4th edition, 2013,    
         Addison Wesley ISBN-10: 0321563840 p464</LI>
  <LI>[2] Martin Reddy, API design for C++, 2011, Elsevier ISBN-10: 0123850037, 
  Section 3.1</LI>
  <LI>[3]         <A href="http://channel9.msdn.com/posts/C-and-Beyond-2012-Herb-Sutter-You-dont-know-blank-and-blank">Herb 
  Sutter, C++ and Beyond 2012: Herb Sutter - You don't know [blank] and 
  [blank]</A></LI>
  <LI>[4]         <A href="http://www.boost.org/doc/libs/1_55_0/libs/utility/value_init.htm">boost 
  value_initialized</A></LI></UL></DIV></BODY></HTML>
