    Doc. no:  N4044
    Date:     2014-05-24
    Revises:  N3603
    Reply-To: Christopher Kohlhoff <chris@kohlhoff.com>

## A Three-Class IP Address Proposal, Revision 1

## 1. Overview

This proposal describes a three-class design for IP address classes:

- A vocabulary type, `ip::address`, for use in IP version independent code.
- An IPv4-specific type `ip::address_v4`.
- An IPv6-specific type `ip::address_v6`.

## 2. Motivation and Scope

### 2.1. Scope

This proposal describes only types necessary to support the manipulation of IP (Internet Protocol) addresses. Other networking facilities, such as sockets and name resolution, are outside the scope of this proposal.

### 2.2. Target Audience

The proposed interface is primarily intended for use by developers writing programs with a networking component, and in particular programs that utilise internet protocols such as TCP and UDP.

### 2.3. Reference Implementation

This proposal is based on the IP address classes in the Boost.Asio library. The proposed text was originally from N2175 "Networking Library Proposal for TR2 (Revision 1)" with small modifications to reflect enhancements made to Boost.Asio since 2007.

In this revision, the proposed text has been updated to reflect feedback and discussion from the Bristol 2013 meeting of SG4. A reference implementation of the proposal is available from GitHub.

## 3. Impact On the Standard

This is a pure library proposal. It does not add any new language features, nor does it alter any existing standard library headers.

This library can be implemented using compilers that conform to the C++11 standard. An implementation of this library requires operating system-specific functions that lie outside the C++11 standard.

## 4. Design Decisions

For more information on the motivating design decisions for the three-class design, please consult N3603.

In this section we list some of the additional proposed changes that were considered but rejected. The changes incorporated since N3603 are described at the end.

### 4.1. Overloads of hton and ntoh

One suggestion was that the proposal should include overloads of `hton` and `ntoh` for the address types. The motivating use case was that address objects may be used directly as part of standard-layout aggregates that are ultimately intended for network transmission.

This change was rejected on the basis that the applying `hton` to a class type (as opposed to a simple integer) may result in an object that is in an invalid or nonsensical state. As a matter of style, the author feels that the address types should be treated as abstractions without necessarily forcing a particular internal implementation.

The addresses' `to_bytes()` member functions can already be used to obtain a representation of the address in network byte order. To further aid the motivating use case, the `bytes_type` types have been changed to be unique types.

### 4.2. Specialisation of less<>

Another suggestion was that the address types should specialise `std::less<>` rather than provide `operator<` and friends. This change was rejected since it would appear to render the address types incompatible with other standard library types, such as `std::tuple<>`, which apply `operator<` to their elements.

## 5. Further Work

### 5.1. Address Iteration

It has been suggested in the SG4 discussion forum that the address classes include the ability to determine the predecessor or successor of an address. By this we mean that given an address 1.2.3.4, the predecessor address is 1.2.3.3 and the successor is 1.2.3.5.

The author feels that this feature is not a core responsibility of the address classes, but if it is desired it can instead be supported by a richer range-based interface. The following is a sketch of this approach:

    class address_iterator_v4
    {
    public:
      typedef std::ptrdiff_t difference_type;
      typedef address_v4 value_type;
      typedef const address_v4* pointer;
      typedef const address_v4& reference;
      typedef std::bidirectional_iterator_tag iterator_category;

      explicit address_iterator_v4(const address_v4& addr);

      const address_v4& operator*() const;
      const address_v4* operator->() const;

      address_iterator_v4& operator++();
      address_iterator_v4 operator++(int);

      address_iterator_v4& operator--();
      address_iterator_v4 operator--(int);

      friend bool operator==(const address_iterator_v4& a, const address_iterator_v4& b);
      friend bool operator!=(const address_iterator_v4& a, const address_iterator_v4& b);

    private:
      // ...
    };

    class address_range_v4
    {
    public:
      address_range_v4();
      explicit address_range_v4(const address_v4& addr);
      explicit address_range_v4(const address_v4& addr, const address_v4& mask);

      address_v4 network() const;
      address_v4 netmask() const;
      address_v4 broadcast() const;

      typedef address_iterator_v4 iterator;
      iterator begin() const;
      iterator end() const;
      iterator find(const address_v4& addr) const;

    private:
      // ...
    };

This may be used as in the following example:

    address_range_v4 range(
        make_address_v4("192.168.1.0"),
        make_address_v4("255.255.255.0"));

    for (auto a: range)
      std::cout << a << std::endl;

    std::cout << "---" << std::endl;

    std::for_each(
        range.find(make_address_v4("192.168.1.250")),
        range.end(),
        [](address_v4 a) { std::cout << a << std::endl; });

If SG4 determines that this feature is desirable, it may be added to this proposal or developed as a separate proposal. The author favours the latter.

## 6. Proposed Text

### 6.1. Definitions

#### 6.1.1. host byte order

See section 1.4 of N3783 Network Byte Order Conversion.

#### 6.1.2. network byte order

See section 1.4 of N3783 Network Byte Order Conversion.

### 6.2. Error reporting

Network library functions often provide two overloads, one that throws an exception to report system errors, and another that sets an `error_code`.

[Note: This supports two common use cases:
— Uses where system errors are truly exceptional and indicate a serious failure. Throwing an exception is the most appropriate response. This is the preferred default for most everyday programming.
— Uses where system errors are routine and do not necessarily represent failure. Returning an error code is the most appropriate response. This allows application specific error handling, including simply ignoring the error.
—end note]

Functions not having an argument of type `error_code&` report errors as follows, unless otherwise specified:

— When a call by the implementation to an operating system or other underlying API results in an error that prevents the function from meeting its specifications, an exception of type `system_error` shall be thrown.

— Failure to allocate storage is reported by throwing an exception as described in the C++ standard (C++14 [res.on.exception.handling]).

— Destructors throw nothing.

Functions having an argument of type `error_code&` report errors as follows, unless otherwise specified:

— If a call by the implementation to an operating system or other underlying API results in an error that prevents the function from meeting its specifications, the `error_code&` argument is set as appropriate for the specific error. Otherwise, `clear()` is called on the `error_code&` argument.

### 6.3. Internet protocol addresses

This clause describes components that C++ programs may use to manipulate IP addresses.

#### 6.3.1. Header `<experimental/net>` synopsis

    namespace std {
      namespace experimental {
        namespace net {

          // Internet protocol addresses:

          namespace ip {

            struct v4_mapped_t {};
            constexpr v4_mapped_t v4_mapped;

            class address;
            class address_v4;
            class address_v6;

            class bad_address_cast;

            // address comparisons:
            bool operator==(const address&, const address&) noexcept;
            bool operator!=(const address&, const address&) noexcept;
            bool operator< (const address&, const address&) noexcept;
            bool operator> (const address&, const address&) noexcept;
            bool operator<=(const address&, const address&) noexcept;
            bool operator>=(const address&, const address&) noexcept;

            // address_v4 comparisons:
            bool operator==(const address_v4&, const address_v4&) noexcept;
            bool operator!=(const address_v4&, const address_v4&) noexcept;
            bool operator< (const address_v4&, const address_v4&) noexcept;
            bool operator> (const address_v4&, const address_v4&) noexcept;
            bool operator<=(const address_v4&, const address_v4&) noexcept;
            bool operator>=(const address_v4&, const address_v4&) noexcept;

            // address_v6 comparisons:
            bool operator==(const address_v6&, const address_v6&) noexcept;
            bool operator!=(const address_v6&, const address_v6&) noexcept;
            bool operator< (const address_v6&, const address_v6&) noexcept;
            bool operator> (const address_v6&, const address_v6&) noexcept;
            bool operator<=(const address_v6&, const address_v6&) noexcept;
            bool operator>=(const address_v6&, const address_v6&) noexcept;

            // address creation:
            address make_address(const char*);
            address make_address(const char*, error_code&) noexcept;
            address make_address(const string&);
            address make_address(const string&, error_code&) noexcept;

            // address_v4 creation:
            constexpr address_v4 make_address_v4(const address_v4::bytes_type&);
            constexpr address_v4 make_address_v4(unsigned long);
            constexpr address_v4 make_address_v4(v4_mapped_t, const address_v6&);
            address_v4 make_address_v4(const char*);
            address_v4 make_address_v4(const char*, error_code&) noexcept;
            address_v4 make_address_v4(const string&);
            address_v4 make_address_v4(const string&, error_code&) noexcept;

            // address_v6 creation:
            constexpr address_v6 make_address_v6(const address_v6::bytes_type&, unsigned long = 0);
            constexpr address_v6 make_address_v6(v4_mapped_t, const address_v4&) noexcept;
            address_v6 make_address_v6(const char*);
            address_v6 make_address_v6(const char*, error_code&) noexcept;
            address_v6 make_address_v6(const string&);
            address_v6 make_address_v6(const string&, error_code&) noexcept;

            // address I/O:
            template<class CharT, class Traits>
              basic_ostream<CharT, Traits>& operator<<(
                basic_ostream<CharT, Traits>&, const address&);

            // address_v4 I/O:
            template<class CharT, class Traits>
              basic_ostream<CharT, Traits>& operator<<(
                basic_ostream<CharT, Traits>&, const address_v4&);

            // address_v6 I/O:
            template<class CharT, class Traits>
              basic_ostream<CharT, Traits>& operator<<(
                basic_ostream<CharT, Traits>&, const address_v6&);

            // address conversions:
            template <class T> constexpr T address_cast(const address&) noexcept(see below);
            template <class T> constexpr T address_cast(const address_v4&) noexcept(see below);
            template <class T> constexpr T address_cast(const address_v6&) noexcept(see below);

          } // namespace ip
        } // namespace net

        // hash support
        template <class T> struct hash;
        template <> struct hash<net::ip::address>;
        template <> struct hash<net::ip::address_v4>;
        template <> struct hash<net::ip::address_v6>;

        inline namespace literals {
          inline namespace net_literals {

            // suffixes for address literals
            net::ip::address operator "" ip(const char*, size_t);
            net::ip::address_v4 operator "" ipv4(const char*, size_t);
            net::ip::address_v6 operator "" ipv6(const char*, size_t);

          } // namespace net_literals
        } // namespace literals
      } // namespace experimental
    } // namespace std

#### 6.3.2. Class `ip::address`

    namespace std {
      namespace experimental {
        namespace net {
          namespace ip {

            class address
            {
            public:
              // constructors:
              constexpr address() noexcept;
              constexpr address(const address& a) noexcept;
              template <class T> constexpr address(const T& t) noexcept(see below);
              template <class... T> explicit constexpr address(T&&... t);

              // assignment:
              address& operator=(const address& a) noexcept;

              // members:
              constexpr bool is_unspecified() const noexcept;
              constexpr bool is_loopback() const noexcept;
              constexpr bool is_multicast() const noexcept;
              constexpr bool is_v4() const noexcept;
              constexpr bool is_v6() const noexcept;
              string to_string() const;
              string to_string(error_code& ec) const;
            };

            // address comparisons:
            bool operator==(const address& a, const address& b) noexcept;
            bool operator!=(const address& a, const address& b) noexcept;
            bool operator< (const address& a, const address& b) noexcept;
            bool operator> (const address& a, const address& b) noexcept;
            bool operator<=(const address& a, const address& b) noexcept;
            bool operator>=(const address& a, const address& b) noexcept;

            // address creation:
            address make_address(const char* str);
            address make_address(const char* str, error_code& ec) noexcept;
            address make_address(const string& str);
            address make_address(const string& str, error_code& ec) noexcept;

            // address I/O:
            template<class CharT, class Traits>
              basic_ostream<CharT, Traits>& operator<<(
                basic_ostream<CharT, Traits>& os, const address& addr);

          } // namespace ip
        } // namespace net
      } // namespace experimental
    } // namespace std

##### 6.3.2.1. `ip::address` constructors

    constexpr address() noexcept;

> Effects: Constructs an object of class `address`.

> Postconditions: The postconditions of this function are indicated in the table below.

Table 1. address::address() effects

expression | value
--- | ---
`is_v4()` | `false`
`is_v6()` | `false`

    constexpr address(const address& a) noexcept;

> Effects: Constructs an object of class `address`.

> Postconditions: `*this == a`

    template <class T> constexpr address(const T& t) noexcept(see below);

> Remarks: This constructor shall not participate in overload resolution unless `is_same<T, address>::value` is `false`, and the expression `address_cast<address>(t)` is valid and yields an rvalue of type `address`. The expression inside `noexcept` shall be equivalent to `noexcept(address_cast<address>(t))`.

> Effects: Constructs an object of type `address` with the result of the expression `address_cast<address>(t)`.

> Throws: Nothing unless the expression `address_cast<address>(t)` throws an exception.

    template <class... T> explicit constexpr address(T&&... t);

> Remarks: This constructor shall not participate in overload resolution unless the expression `make_address(forward<T>(t).``.``.)` is valid and yields an rvalue of type `address`.

> Effects: Constructs an object of type `address` with the result of the expression `make_address(forward<T>(t).``.``.)`.

##### 6.3.2.2. `ip::address` assignment

    address& operator=(const address& a) noexcept;

> Postconditions: `*this == a`

> Returns: `*this`

##### 6.3.2.3. `ip::address` members

    constexpr bool is_unspecified() const noexcept;

> Returns: If `is_v4() == true`, returns `address_cast<address_v4>(*this).is_unspecified()`. If `is_v6() == true`, returns `address_cast<address_v6>(*this).is_unspecified()`. Otherwise returns `false`.

    constexpr bool is_loopback() const noexcept;

> Returns: If `is_v4() == true`, returns `address_cast<address_v4>(*this).is_loopback()`. If `is_v6() == true`, returns `address_cast<address_v6>(*this).is_loopback()`. Otherwise returns `false`.

    constexpr bool is_multicast() const noexcept;

> Returns: If `is_v4() == true`, returns `address_cast<address_v4>(*this).is_multicast()`. If `is_v6() == true`, returns `address_cast<address_v6>(*this).is_multicast()`. Otherwise returns `false`.

    constexpr bool is_v4() const noexcept;

> Returns: `true` if the object contains an IP version 4 address.

    constexpr bool is_v6() const noexcept;

> Returns: `true` if the object contains an IP version 6 address.

    string to_string() const;
    string to_string(error_code& ec) const;

> Returns: If `is_v4() == true`, returns `address_cast<address_v4>(*this).to_string(ec)`. If `is_v6() == true`, returns `address_cast<address_v6>(*this).to_string(ec)`. Otherwise throws `bad_address_cast`.

##### 6.3.2.4. `ip::address` comparisons

    bool operator==(const address& a, const address& b) noexcept;

> Returns: `a_v4 == b_v4 && a_v6 == b_v6`, where:
> — `a_v4` is the value of `a.is_v4() ? address_cast<address_v4>(a) : address_v4()`;
> — `b_v4` is `b.is_v4() ? address_cast<address_v4>(b) : address_v4()`;
> — `a_v6` is `a.is_v6() ? address_cast<address_v6>(a) : address_v6()`; and
> — `b_v6` is `b.is_v6() ? address_cast<address_v6>(b) : address_v6()`.

    bool operator!=(const address& a, const address& b) noexcept;

> Returns: `!(a == b)`.

    bool operator< (const address& a, const address& b) noexcept;

> Returns: `a_v6 < b_v6 || a_v6 == b_v6 && a_v4 < b_v4`, where:
> — `a_v4` is the value of `a.is_v4() ? address_cast<address_v4>(a) : address_v4()`;
> — `b_v4` is `b.is_v4() ? address_cast<address_v4>(b) : address_v4()`;
> — `a_v6` is `a.is_v6() ? address_cast<address_v6>(a) : address_v6()`; and
> — `b_v6` is `b.is_v6() ? address_cast<address_v6>(b) : address_v6()`.

    bool operator> (const address& a, const address& b) noexcept;

> Returns: `b < a`.

    bool operator<=(const address& a, const address& b) noexcept;

> Returns: `!(b < a)`.

    bool operator>=(const address& a, const address& b) noexcept;

> Returns: `!(a < b)`.

##### 6.3.2.5. `ip::address` creation

    address make_address(const char* str);
    address make_address(const char* str, error_code& ec) noexcept;
    address make_address(const string& str);
    address make_address(const string& str, error_code& ec) noexcept;

> Effects: Converts a string representation of an address into an object of class `address`, as if by calling:

>     address a;
>     address_v6 v6a = make_address_v6(str, ec);
>     if (!ec)
>       a = v6a;
>     else
>     {
>       address_v4 v4a = make_address_v4(str, ec);
>       if (!ec)
>         a = v4a;
>     }

> Returns: `a`.

##### 6.3.2.6. `ip::address` I/O

    template<class CharT, class Traits>
      basic_ostream<CharT, Traits>& operator<<(
        basic_ostream<CharT, Traits>& os, const address& addr);

> Effects: Outputs the string representation of the address to the stream, as if it were implemented as follows:

>     error_code ec;
>     string s = addr.to_string(ec);
>     if (ec)
>     {
>       if (os.exceptions() & ios_base::failbit)
>         throw system_error(ec);
>       else
>         os.setstate(ios_base::failbit);
>     }
>     else
>       for (string::iterator i = s.begin(); i != s.end(); ++i)
>         os << os.widen(*i);

> Returns: `os`.

#### 6.3.3. Class `ip::address_v4`

    namespace std {
      namespace experimental {
        namespace net {
          namespace ip {

            class address_v4
            {
            public:
              // types:
              struct bytes_type;

              // constructors:
              constexpr address_v4() noexcept;
              constexpr address_v4(const address_v4& a) noexcept;
              constexpr address_v4(const bytes_type& bytes);
              template <class... T> explicit constexpr address(T&&... t);

              // assignment:
              address_v4& operator=(const address_v4& a) noexcept;

              // members:
              constexpr bool is_unspecified() const noexcept;
              constexpr bool is_loopback() const noexcept;
              constexpr bool is_class_a() const noexcept;
              constexpr bool is_class_b() const noexcept;
              constexpr bool is_class_c() const noexcept;
              constexpr bool is_multicast() const noexcept;
              constexpr bytes_type to_bytes() const noexcept;
              constexpr unsigned long to_ulong() const noexcept;
              string to_string() const;
              string to_string(error_code& ec) const;

              // static members:
              static constexpr address_v4 any() noexcept;
              static constexpr address_v4 loopback() noexcept;
              static constexpr address_v4 broadcast() noexcept;
              static constexpr address_v4 broadcast(const address_v4& addr,
                const address_v4& mask) noexcept;
            };

            // address_v4 comparisons:
            bool operator==(const address_v4& a, const address_v4& b) noexcept;
            bool operator!=(const address_v4& a, const address_v4& b) noexcept;
            bool operator< (const address_v4& a, const address_v4& b) noexcept;
            bool operator> (const address_v4& a, const address_v4& b) noexcept;
            bool operator<=(const address_v4& a, const address_v4& b) noexcept;
            bool operator>=(const address_v4& a, const address_v4& b) noexcept;

            // address_v4 creation:
            constexpr address_v4 make_address_v4(const address_v4::bytes_type& bytes);
            constexpr address_v4 make_address_v4(unsigned long val);
            constexpr address_v4 make_address_v4(v4_mapped_t, const address_v6& a);
            address_v4 make_address_v4(const char* str);
            address_v4 make_address_v4(const char* str, error_code& ec) noexcept;
            address_v4 make_address_v4(const string& str);
            address_v4 make_address_v4(const string& str, error_code& ec) noexcept;

            // address_v4 I/O:
            template<class CharT, class Traits>
              basic_ostream<CharT, Traits>& operator<<(
                basic_ostream<CharT, Traits>& os, const address_v4& addr);

          } // namespace ip
        } // namespace net
      } // namespace experimental
    } // namespace std

##### 6.3.3.1. Struct `ip::address_v4::bytes_type`

    namespace std {
      namespace experimental {
        namespace net {
          namespace ip {

            struct address_v4::bytes_type : array<unsigned char, 4>
            {
              template <class... T> explicit constexpr bytes_type(T... t)
                : array<unsigned char, 4>{{static_cast<unsigned char>(t)...}} {}
            };

          } // namespace ip
        } // namespace net
      } // namespace experimental
    } // namespace std

The `ip::address_v4::bytes_type` type is a standard-layout struct that provides a byte-level representation of an IPv4 address in network byte order.

##### 6.3.3.2. `ip::address_v4` constructors

    constexpr address_v4() noexcept;

> Effects: Constructs an object of class `address_v4`.

> Postconditions: The postconditions of this function are indicated in the table below.

Table 2. address_v4::address_v4() effects

expression | value
--- | ---
`to_bytes()` | `{0, 0, 0, 0}`
`to_ulong()` | `0`

    constexpr address_v4(const address_v4& a) noexcept;

> Effects: Constructs an object of class `address_v4`.

> Postconditions: `*this == a`

    constexpr address_v4(const bytes_type& bytes);

> Requires: Each element of `bytes` is in the range `[0, 0xFF]`.

> Throws: `out_of_range` if any element of `bytes` is not in the range `[0, 0xFF]`. [Note: For implementations where `UCHAR_MAX == 0xFF`, no out-of-range detection is needed. —end note]

> Postconditions: `to_bytes() == bytes` and `to_ulong() == (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]`.

    template <class... T> explicit constexpr address_v4(T&&... t);

> Remarks: This constructor shall not participate in overload resolution unless the expression `make_address_v4(forward<T>(t).``.``.)` is valid and yields an rvalue of type `address_v4`.

> Effects: Constructs an object of type `address_v4` with the result of the expression `make_address_v4(forward<T>(t).``.``.)`.

##### 6.3.3.3. `ip::address_v4` assignment

    address_v4& operator=(const address_v4& a) noexcept;

> Postconditions: `*this == a`

> Returns: `*this`

##### 6.3.3.4. `ip::address_v4` members

    constexpr bool is_unspecified() const noexcept;

> Returns: `to_ulong() == 0`.

    constexpr bool is_loopback() const noexcept;

> Returns: `(to_ulong() & 0xFF000000) == 0x7F000000`.

    constexpr bool is_class_a() const noexcept;

> Returns: `(to_ulong() & 0x80000000) == 0`.

    constexpr bool is_class_b() const noexcept;

> Returns: `(to_ulong() & 0xC0000000) == 0x80000000`.

    constexpr bool is_class_c() const noexcept;

> Returns: `(to_ulong() & 0xE0000000) == 0xC0000000`.

    constexpr bool is_multicast() const noexcept;

> Returns: `(to_ulong() & 0xF0000000) == 0xE0000000`.

    constexpr bytes_type to_bytes() const noexcept;

> Returns: A representation of the address in network byte order.

    constexpr unsigned long to_ulong() const noexcept;

> Returns: A representation of the address in host byte order.

    string to_string() const;
    string to_string(error_code& ec) const;

> Effects: Converts an address into a string representation, as if by POSIX `inet_ntop()` when invoked with address family `AF_INET`.

> Returns: If successful, the string representation of the address. Otherwise `string()`.

##### 6.3.3.5. `ip::address_v4` static members

    static constexpr address_v4 any() noexcept;

> Returns: `address_v4()`.

    static constexpr address_v4 loopback() noexcept;

> Returns: `address_v4(0x7F000001)`.

    static constexpr address_v4 broadcast() noexcept;

> Returns: `address_v4(0xFFFFFFFF)`.

    static constexpr address_v4 broadcast(const address_v4& addr, const address_v4& mask) noexcept;

> Returns: `address_v4(addr.to_ulong() | ~mask.to_ulong())`.

##### 6.3.3.6. `ip::address_v4` comparisons

    bool operator==(const address_v4& a, const address_v4& b) noexcept;

> Returns: `a.to_ulong() == b.to_ulong()`.

    bool operator!=(const address_v4& a, const address_v4& b) noexcept;

> Returns: `a.to_ulong() != b.to_ulong()`.

    bool operator< (const address_v4& a, const address_v4& b) noexcept;

> Returns: `a.to_ulong() < b.to_ulong()`.

    bool operator> (const address_v4& a, const address_v4& b) noexcept;

> Returns: `a.to_ulong() > b.to_ulong()`.

    bool operator<=(const address_v4& a, const address_v4& b) noexcept;

> Returns: `a.to_ulong() <= b.to_ulong()`.

    bool operator>=(const address_v4& a, const address_v4& b) noexcept;

> Returns: `a.to_ulong() >= b.to_ulong()`.

##### 6.3.3.7. `ip::address_v4` creation

    constexpr address_v4 make_address_v4(const address_v4::bytes_type& bytes);

> Returns: `address_v4(bytes)`.

    constexpr address_v4 make_address_v4(unsigned long val);

> Requires: `val` is in the range `[0, 0xFFFFFFFF]`.

> Throws: `out_of_range` if `val` is not in the range `[0, 0xFFFFFFFF]`. [Note: For implementations where `ULONG_MAX == 0xFFFFFFFF`, no out-of-range detection is needed. —end note]

> Returns: An object `a` of type `address_v4` where `a.to_ulong() == val` and `a.to_bytes()` is `{ (val >> 24) & 0xFF, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF }`.

    constexpr address_v4 make_address_v4(v4_mapped_t, const address_v6& a);

> Requires: `a.is_v4_mapped()`.

> Returns: An `address_v4` object corresponding to the IPv4-mapped IPv6 address, as if computed by the following method:

>     bytes_type v6b = a.to_bytes();
>     address_v4::bytes_type v4b(v6b[12], v6b[13], v6b[14], v6b[15]);
>     return address_v4(v4b);

> Throws: `bad_address_cast` if `a.is_v4_mapped()` is false.

    address_v4 make_address_v4(const char* str);
    address_v4 make_address_v4(const char* str, error_code& ec) noexcept;
    address_v4 make_address_v4(const string& str);
    address_v4 make_address_v4(const string& str, error_code& ec) noexcept;

> Effects: Converts a string representation of an address into a corresponding `address_v4` value, as if by POSIX `inet_pton()` when invoked with address family `AF_INET`.

> Returns: If successful, an `address_v4` value corresponding to the string `str`. Otherwise `address_v4()`.

##### 6.3.3.8. `ip::address_v4` I/O

    template<class CharT, class Traits>
      basic_ostream<CharT, Traits>& operator<<(
        basic_ostream<CharT, Traits>& os, const address_v4& addr);

> Effects: Outputs the string representation of the address to the stream, as if it were implemented as follows:

>     error_code ec;
>     string s = addr.to_string(ec);
>     if (ec)
>     {
>       if (os.exceptions() & ios_base::failbit)
>         throw system_error(ec);
>       else
>         os.setstate(ios_base::failbit);
>     }
>     else
>       for (string::iterator i = s.begin(); i != s.end(); ++i)
>         os << os.widen(*i);

> Returns: `os`.

#### 6.3.4. Class `ip::address_v6`

    namespace std {
      namespace experimental {
        namespace net {
          namespace ip {

            class address_v6
            {
            public:
              // types:
              struct bytes_type;

              // constructors:
              constexpr address_v6() noexcept;
              constexpr address_v6(const address_v6& a) noexcept;
              constexpr address_v6(const bytes_type& bytes, unsigned long scope);
              template <class... T> explicit constexpr address(T&&... t);

              // assignment:
              address_v4& operator=(const address_v4& a) noexcept;

              // members:
              void scope_id(unsigned long id) noexcept;
              constexpr unsigned long scope_id() const noexcept;
              constexpr bool is_unspecified() const noexcept;
              constexpr bool is_loopback() const noexcept;
              constexpr bool is_multicast() const noexcept;
              constexpr bool is_link_local() const noexcept;
              constexpr bool is_site_local() const noexcept;
              constexpr bool is_v4_mapped() const noexcept;
              constexpr bool is_multicast_node_local() const noexcept;
              constexpr bool is_multicast_link_local() const noexcept;
              constexpr bool is_multicast_site_local() const noexcept;
              constexpr bool is_multicast_org_local() const noexcept;
              constexpr bool is_multicast_global() const noexcept;
              constexpr bytes_type to_bytes() const noexcept;
              string to_string() const;
              string to_string(error_code& ec) const;

              // static members:
              static constexpr address_v6 any() noexcept;
              static constexpr address_v6 loopback() noexcept;
            };

            // address_v6 comparisons:
            bool operator==(const address_v6& a, const address_v6& b) noexcept;
            bool operator!=(const address_v6& a, const address_v6& b) noexcept;
            bool operator< (const address_v6& a, const address_v6& b) noexcept;
            bool operator> (const address_v6& a, const address_v6& b) noexcept;
            bool operator<=(const address_v6& a, const address_v6& b) noexcept;
            bool operator>=(const address_v6& a, const address_v6& b) noexcept;

            // address_v6 creation:
            constexpr address_v6 make_address_v6(const address_v6::bytes_type& bytes,
                                                 unsigned long scope_id = 0);
            constexpr address_v6 make_address_v6(v4_mapped_t, const address_v4& a) noexcept;
            address_v6 make_address_v6(const char* str);
            address_v6 make_address_v6(const char* str, error_code& ec) noexcept;
            address_v6 make_address_v6(const string& str);
            address_v6 make_address_v6(const string& str, error_code& ec) noexcept;

            // address_v6 I/O:
            template<class CharT, class Traits>
              basic_ostream<CharT, Traits>& operator<<(
                basic_ostream<CharT, Traits>& os, const address_v6& addr);

          } // namespace ip
        } // namespace net
      } // namespace experimental
    } // namespace std

[Note: The implementations of the functions `is_unspecified`, `is_loopback`, `is_multicast`, `is_link_local`, `is_site_local`, `is_v4_mapped`, `is_multicast_node_local`, `is_multicast_link_local`, `is_multicast_site_local`, `is_multicast_org_local` and `is_multicast_global` are determined by [RFC4291]. —end note]

##### 6.3.4.1. Struct `ip::address_v6::bytes_type`

    namespace std {
      namespace experimental {
        namespace net {
          namespace ip {

            struct address_v6::bytes_type : array<unsigned char, 16>
            {
              template <class... T> explicit constexpr bytes_type(T... t)
                : array<unsigned char, 16>{{static_cast<unsigned char>(t)...}} {}
            };

          } // namespace ip
        } // namespace net
      } // namespace experimental
    } // namespace std

The `ip::address_v6::bytes_type` type is a standard-layout struct that provides a byte-level representation of an IPv6 address in network byte order.

##### 6.3.4.2. `ip::address_v6` constructors

    constexpr address_v6() noexcept;

> Effects: Constructs an object of class `address_v6`.

> Postconditions: The postconditions of this function are indicated in the table below.

Table 3. address_v6::address_v6() effects

expression | value
--- | ---
`is_unspecified()` | `true`
`scope_id()` | `0`

    constexpr address_v6(const address_v6& a) noexcept;

> Effects: Constructs an object of class `address_v6`.

> Postconditions: `*this == a`

    constexpr address_v6(const bytes_type& bytes, unsigned long scope);

> Requires: Each element of `bytes` is in the range `[0, 0xFF]`.

> Throws: `out_of_range` if any element of `bytes` is not in the range `[0, 0xFF]`. [Note: For implementations where `UCHAR_MAX == 0xFF`, no out-of-range detection is needed. —end note]

> Postconditions: `to_bytes() == bytes` and `scope_id() == scope`.

    template <class... T> explicit constexpr address_v6(T&&... t);

> Remarks: This constructor shall not participate in overload resolution unless the expression `make_address_v6(forward<T>(t).``.``.)` is valid and yields an rvalue of type `address_v6`.

> Effects: Constructs an object of type `address_v6` with the result of the expression `make_address_v6(forward<T>(t).``.``.)`.

##### 6.3.4.3. `ip::address_v6` assignment

    address_v6& operator=(const address_v6& a) noexcept;

> Postconditions: `*this == a`

> Returns: `*this`

##### 6.3.4.4. `ip::address_v6` members

    void scope_id(unsigned long id) noexcept;

> Postconditions: `scope_id() == id`.

    constexpr unsigned long scope_id() const noexcept;

> Returns: The scope identifier associated with the address.

    constexpr bool is_unspecified() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents an unspecified address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[ 0] == 0 && b[ 1] == 0 && b[ 2] == 0 && b[ 3] == 0
>         && b[ 4] == 0 && b[ 5] == 0 && b[ 6] == 0 && b[ 7] == 0
>         && b[ 8] == 0 && b[ 9] == 0 && b[10] == 0 && b[11] == 0
>         && b[12] == 0 && b[13] == 0 && b[14] == 0 && b[15] == 0;

    constexpr bool is_loopback() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents a loopback address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[ 0] == 0 && b[ 1] == 0 && b[ 2] == 0 && b[ 3] == 0
>         && b[ 4] == 0 && b[ 5] == 0 && b[ 6] == 0 && b[ 7] == 0
>         && b[ 8] == 0 && b[ 9] == 0 && b[10] == 0 && b[11] == 0
>         && b[12] == 0 && b[13] == 0 && b[14] == 0 && b[15] == 1;

    constexpr bool is_multicast() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents a multicast address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[0] == 0xFF;

    constexpr bool is_link_local() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents a unicast link-local address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[0] == 0xFE && (b[1] & 0xC0) == 0x80;

    constexpr bool is_site_local() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents a unicast site-local address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[0] == 0xFE && (b[1] & 0xC0) == 0xC0;

    constexpr bool is_v4_mapped() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents an IPv4-mapped IPv6 address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[ 0] == 0 && b[ 1] == 0 && b[ 2] == 0    && b[ 3] == 0
>         && b[ 4] == 0 && b[ 5] == 0 && b[ 6] == 0    && b[ 7] == 0
>         && b[ 8] == 0 && b[ 9] == 0 && b[10] == 0xFF && b[11] == 0xFF;

    constexpr bool is_multicast_node_local() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents a multicast node-local address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[0] == 0xFF && (b[1] & 0x0F) == 0x01;

    constexpr bool is_multicast_link_local() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents a multicast link-local address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[0] == 0xFF && (b[1] & 0x0F) == 0x02;

    constexpr bool is_multicast_site_local() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents a multicast site-local address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[0] == 0xFF && (b[1] & 0x0F) == 0x05;

    constexpr bool is_multicast_org_local() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents a multicast organisation-local address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[0] == 0xFF && (b[1] & 0x0F) == 0x08;

    constexpr bool is_multicast_global() const noexcept;

> Returns: A boolean indicating whether the `address_v6` object represents a multicast global address, as if computed by the following method:

>     bytes_type b = to_bytes();
>     return b[0] == 0xFF && (b[1] & 0x0F) == 0x0E;

    constexpr bytes_type to_bytes() const noexcept;

> Returns: A representation of the address in network byte order.

    string to_string() const;
    string to_string(error_code& ec) const;

> Effects: Converts an address into a string representation. If `scope_id() == 0`, converts as if by POSIX `inet_ntop()` when invoked with address family `AF_INET6`. If `scope_id() != 0`, the format is `address``%``scope-id`, where `address` is the string representation of the equivalent address having `scope_id() == 0`, and `scope-id` is an implementation-defined string representation of the scope identifier.

> Returns: If successful, the string representation of the address. Otherwise `string()`.

##### 6.3.4.5. `ip::address_v6` static members

    static constexpr address_v6 any() noexcept;

> Returns: `address_v6()`.

    static constexpr address_v6 loopback() noexcept;

> Returns: An address `a` such that the condition `a.is_loopback()` holds.

##### 6.3.4.6. `ip::address_v6` comparisons

    bool operator==(const address_v6& a, const address_v6& b) noexcept;

> Returns: `a.to_bytes() == b.to_bytes() && a.scope_id() == b.scope_id()`.

    bool operator!=(const address_v6& a, const address_v6& b) noexcept;

> Returns: `!(a == b)`.

    bool operator< (const address_v6& a, const address_v6& b) noexcept;

> Returns: `a.to_bytes() < b.to_bytes() || (!(b.to_bytes() < a.to_bytes()) && a.scope_id() < b.scope_id())`.

    bool operator> (const address_v6& a, const address_v6& b) noexcept;

> Returns: `b < a`.

    bool operator<=(const address_v6& a, const address_v6& b) noexcept;

> Returns: `!(b < a)`.

    bool operator>=(const address_v6& a, const address_v6& b) noexcept;

> Returns: `!(a < b)`.

##### 6.3.4.7. `ip::address_v6` creation

    constexpr address_v6 make_address_v6(const address_v6::bytes_type& bytes,
                                         unsigned long scope_id);

> Returns: `address_v6(bytes, scope_id)`.

    constexpr address_v6 make_address_v6(v4_mapped_t, const address_v4& a) noexcept;

> Returns: An `address_v6` object containing the IPv4-mapped IPv6 address corresponding to the specified IPv4 address, as if computed by the following method:

>     address_v4::bytes_type v4b = a.to_bytes();
>     bytes_type v6b(0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
>                    0xFF, 0xFF, v4b[0], v4b[1], v4b[2], v4b[3]);
>     return address_v6(v6b);

    address_v6 make_address_v6(const char* str);
    address_v6 make_address_v6(const char* str, error_code& ec) noexcept;
    address_v6 make_address_v6(const string& str);
    address_v6 make_address_v6(const string& str, error_code& ec) noexcept;

> Effects: Converts a string representation of an address into a corresponding `address_v6` value. The format is either `address` or `address``%``scope-id`, where `address` is in the format specified by POSIX `inet_pton()` when invoked with address family `AF_INET6`, and `scope-id` is an optional string specifying the scope identifier. All implementations shall accept as `scope-id` a string representation of an unsigned decimal integer. It is implementation-defined whether alternative scope identifier representations are permitted. If `scope-id` is not supplied, an `address_v6` object shall be returned such that `scope_id() == 0`.

> Returns: If successful, an `address_v6` value corresponding to the string `str`. Otherwise returns `address_v6()`.

##### 6.3.4.8. `ip::address_v6` I/O

    template<class CharT, class Traits>
      basic_ostream<CharT, Traits>& operator<<(
        basic_ostream<CharT, Traits>& os, const address_v6& addr);

> Effects: Outputs the string representation of the address to the stream, as if it were implemented as follows:

>     error_code ec;
>     string s = addr.to_string(ec);
>     if (ec)
>     {
>       if (os.exceptions() & ios_base::failbit)
>         throw system_error(ec);
>       else
>         os.setstate(ios_base::failbit);
>     }
>     else
>       for (string::iterator i = s.begin(); i != s.end(); ++i)
>         os << os.widen(*i);

> Returns: `os`.

#### 6.3.5. Class `ip::bad_address_cast`

    namespace std {
      namespace experimental {
        namespace net {
          namespace ip {

            class bad_address_cast : bad_cast
            {
            public:
              virtual const char* what() const noexcept;
            };

          } // namespace ip
        } // namespace net
      } // namespace experimental
    } // namespace std

Objects of type `bad_address_cast` are thrown by a failed `address_cast`.

#### 6.3.6. Function `ip::address_cast`

    template <class T> constexpr T address_cast(const address& a) noexcept(see below);

> This function template shall participate in overload resolution only for the types `T` listed in the table below.

Table 4. template <class T> constexpr T address_cast(const address&) effects

T | noexcept | remarks
--- | --- | ---
`address` | `true` | Returns `a`.
`address_v4` | `false` | If `a.is_v4()` is `true`, returns the IP version 4 address specified in the version-independent address object `a`. Otherwise, throws `bad_address_cast`.
`address_v6` | `false` | If `a.is_v6()` is `true`, returns the IP version 6 address specified in the version-independent address object `a`. Otherwise, throws `bad_address_cast`.

    template <class T> constexpr T address_cast(const address_v4& a) noexcept(see below);

> This function template shall participate in overload resolution only for the types `T` listed in the table below.

Table 5. template <class T> constexpr T address_cast(const address_v4&) effects

T | noexcept | remarks
--- | --- | ---
`address` | `true` | Returns a version-independent `address` object `b` such that `b.is_v4()` is `true`, `b.is_v6()` is `false`, and `address_cast<address_v4>(b) == a`.
`address_v4` | `true` | Returns `a`.
`address_v6` |  | Function overload is deleted.

    template <class T> constexpr T address_cast(const address_v6& a) noexcept(see below);

> This function template shall participate in overload resolution only for the types `T` listed in the table below.

Table 6. template <class T> constexpr T address_cast(const address_v6&) effects

T | noexcept | remarks
--- | --- | ---
`address` | `true` | Returns a version-independent `address` object `b` such that `b.is_v4()` is `false`, `b.is_v6()` is `true`, and `address_cast<address_v6>(b) == a`.
`address_v4` |  | Function overload is deleted.
`address_v6` | `true` | Returns `a`.

#### 6.3.7. Hash support

    template <> struct hash<net::ip::address>;
    template <> struct hash<net::ip::address_v4>;
    template <> struct hash<net::ip::address_v6>;

> Requires: the template specializations shall meet the requirements of class template `hash` (C++14 [unord.hash]).

#### 6.3.8. Suffixes for address literals

This section describes literal suffixes for constructing address literals. The suffixes `ip`, `ipv4` and `ipv6` denote address values of the corresponding types `ip::address`, `ip::address_v4` and `ip::address_v6` respectively if they are applied to string literals.

[Example: The following code shows some address literals.

    using namespace std::experimental::net_literals;
    auto addr = "127.0.0.1"ip;
    auto v4_addr = "255.255.255.255"ipv4;
    auto v6_addr = "::1"ipv6;

—end example]

    net::ip::address operator "" ip(const char* str, size_t);

> Returns: `net::ip::make_address(str)`.

    net::ip::address_v4 operator "" ipv4(const char* str, size_t);

> Returns: `net::ip::make_address_v4(str)`.

    net::ip::address_v6 operator "" ipv6(const char* str, size_t);

> Returns: `net::ip::make_address_v6(str)`.

## 7. Changes in This Revision

In this revision of the proposal, we include changes made based on feedback and discussion from the 2013 meeting of SG4 at Bristol, where N3603 was discussed.

### 7.1. Namespace

This revision moves the proposed types and functions from the `std::` namespace to `std::experimental::`.

### 7.2. A Version-Independent Vocabulary Type

The `ip::address` class has been modified so that the version-specific types do not appear in its interface. An approach based on the `any` class has been adopted (see Library Fundamentals working draft), and address types may be explicitly converted using the `address_cast<>` function. The version-specific types are implicitly convertible to `ip::address` via a template constructor.

For example:

    address_v4 a;
    address b = a; // ok, implicit conversion
    address c = address_cast<address>(a); // ok, explicit conversion
    address_v4 d = address_cast<address_v4>(b); // ok, explicit conversion
    address_v6 e = address_cast<address_v6>(b); // invalid, throws bad_address_cast

### 7.3. Use of noexcept

Many functions have now been marked `noexcept`.

### 7.4. Explicit Creation from Strings

This replaces the `from_string()` static member functions with new free functions `make_address()`, `make_address_v4()` and `make_address_v6()`. These functions allow explicit creation of the address types from strings and other types, such as an array of bytes or an integer in host byte order. For example, one overload throws an exception on failure:

    address_v4 a = make_address_v4("127.0.0.1"); // succeeds
    address_v4 b = make_address_v4("::1"); // throws

and another overload provides a non-throwing alternative:

    error_code ec;
    address_v4 b = make_address_v4("127.0.0.1", ec); // succeeds, clears ec
    address_v4 b = make_address_v4("::1", ec); // sets ec

An explicit template constructor has been added to also support this style of use:

    address c("127.0.0.1");

This constructor simply forwards the arguments to the appropriate `make_address()` overload.

### 7.5. String Literals

Support for IP address literals has been included. For example:

    using namespace std::experimental::net_literals;
    auto addr = "127.0.0.1"ip;
    auto v4_addr = "255.255.255.255"ipv4;
    auto v6_addr = "::1"ipv6;

### 7.6. Strongly-Typed Byte Representations

The nested typedefs `bytes_type` have been changed to be unique standard-layout types. Objects of type `bytes_type` are implicitly convertible to the corresponding address class. This is intended to support the following use case, where the byte representations of addresses are included as part of a larger standard-layout structure, such as a packet definition:

    struct header
    {
      address_v4::bytes_type from;
      address_v4::bytes_type to;
      // ...
    };

    struct message
    {
      header hdr;
      // ...
    };

    // ...

    address_v4 old_address = "1.2.3.4"ipv4;
    address_v4 new_address = "4.3.2.1"ipv4;

    message m;
    receive_message(m); // e.g. reads bytes from a socket
    if (m.hdr.to == old_address)
      m.hdr.to = new_address.to_bytes();

### 7.7. Use of constexpr

The `constexpr` keyword has been applied to some of the functions in the proposal. The rationale for this change is that many of the equivalent POSIX macros can be used in constant expressions.

### 7.8. Make Default-Constructed address Invalid

A default-constructed `address` object now represents an invalid address that is neither IPv4 nor IPv6. Any attempt to `address_cast` it to another type will result in a `bad_address_cast` exception.

### 7.9. Removed address_v4::netmask

The `ip::address_v4::netmask` function has been removed as it is not possible to give the correct answer, given only an arbitrary IP address, without access to the routing table.

## 8. Acknowledgements

The author would like to thank Arash Partow, Jamie Allsop, James Anderson and Kyle Kloepper for their feedback and suggestions.

## 9. References

[RFC4291] Hinden, R. and Deering, S., RFC 4291: Internet Protocol Version 6 (IPv6) Addressing Architecture, 2006, http://www.ietf.org/rfc/rfc4291.txt.