c++ 为什么std::optional没有对引用类型进行专门化?

2guxujil  于 12个月前  发布在  其他
关注(0)|答案(6)|浏览(198)

为什么std::optionalstd::experimental::optional目前在libc++中)没有对引用类型的专门化(与boost::optional相比)?
我认为这将是非常有用的选择。
在标准库中是否有一些对象 * 引用了
可能
已经存在的对象 * 语义?

1qczuiv0

1qczuiv01#

当n3406(提案的修订版#2)进行了讨论,一些委员会成员对可选引用感到不安。(修订版#3),作者决定将可选引用作为一个辅助建议,以增加可选值获得批准并放入C14的机会。虽然由于各种其他原因,可选值并没有完全进入C14,委员会并不拒绝任择提及,今后如果有人提出,委员会可自由增加任择提及。

ru9i0ody

ru9i0ody2#

std::optional <T&>的主要问题是-optRef = obj在以下情况下应该怎么做:

optional<T&> optRef;
…;
T obj {…};
optRef = obj; // <-- here!

字符串
变体:
1.始终重新绑定-(&optRef)->~optional(); new (&optRef) optional<T&>(obj)
1.通过-*optRef = obj赋值(UB在!optRef之前时)。
1.如果为空则绑定,否则通过-if (optRef) {do1;} else {do2;}赋值。
1.没有赋值运算符-编译时错误“尝试使用已删除的运算符”。
各款产品的优点:

  • 始终重新绑定(由boost::optionaln1878选择):
  • 始终满足!optRefoptRef.has_value()-后置条件**&***optRef == **&**obj时的情况之间的一致性。
  • 与通常的optional<T>在以下方面的一致性:对于通常的optional<T>,如果T::operator=被定义为破坏和构造(一些人认为它 * 必须 * 只不过是破坏和构造的优化),opt = … * 事实上 * 的行为类似于(&opt)->~optional(); new (&opt) optional<T&>(obj)
  • 通过以下方式分配:
  • 与纯T&在以下方面保持一致:对于纯T&ref = …通过赋值(而不是重新绑定ref)。
  • 与通常的optional<T>在以下方面保持一致:对于通常的optional<T>,当opt.has_value()时,需要通过opt = …进行赋值,而不是破坏并构造(参见n3672中的template <class U> optional<T>& optional<T>::operator=(U&& v)on cppreference.com)。
  • 与通常的optional<T>在以下方面保持一致:两者都至少以某种方式定义了operator=
  • 如果为空就绑定,否则赋值--我看不出有什么真实的的好处,恕我直言,只有当#1的支持者与#2的支持者争论时,这个变体才会出现,不管它在形式上与template <class U> optional<T>& optional<T>::operator=(U&& v)的要求更一致(但不符合其精神,恕我直言)。
  • 无赋值运算符(由n3406选择):
  • 与纯T&在以下方面保持一致:纯T&不允许重新绑定自身。
  • 没有模棱两可的行为。

另请参阅:

8yparm6h

8yparm6h3#

确实有一些东西具有 reference tomaybeexisting object 的语义。它被称为(const)pointer。一个普通的旧的无所有权指针。reference和pointer之间有三个区别:
1.指针可以为空,引用不能。这正是std::optional要避免的区别。
1.指针可以被重定向指向其他东西。让它成为常量,这种差异也就消失了。
1.引用不需要被->*解引用。这是纯粹的语法糖,因为1是可能的。指针语法(解引用并可转换为bool)正是std::optional提供的访问值和测试其存在的语法。

更新:optional是一个值容器。像其他容器(例如vector)一样,它不是 * 设计 * 来包含引用的。如果你想要一个可选的引用,使用指针,或者如果你确实需要一个与std::optional语法相似的接口,为指针创建一个小的(和普通的) Package 器。
**更新2:**至于问题 * 为什么 * 没有这样的专业化:因为委员会只是选择了它。理由 * 可能 * 在文件中找到。这可能是因为他们认为指针就足够了。

gojuced7

gojuced74#

恕我直言,让std::optional<T&>可用是非常好的。然而,关于模板有一个微妙的问题。如果有引用,模板参数可能会变得棘手。
正如我们解决模板参数中引用问题的方法一样,我们可以使用std::reference_wrapper来规避std::optional<T&>的缺失。所以现在它变成了std::optional<std::reference_wrapper<T>>。但是我建议不要使用这种方法,因为1)它太冗长了,既要写签名,(尾随返回类型为我们节省了一点)和它的使用(我们必须调用std::reference_wrapper<T>::get()来获取真实的引用),(二)大多数程序员已经被指针折磨过了,所以当他们收到一个指针时,他们就像本能的React一样,首先测试它是否为空。现在不是什么大问题了

q1qsirdb

q1qsirdb5#

如果让我大胆猜测的话,那是因为std::experimental::optional规范中的这句话。(第5.2节,p1)
如果一个程序需要示例化引用类型的模板optional,或者可能是cv限定的类型in_place_tnullopt_t,那么这个程序就是病态的。

nbnkbykc

nbnkbykc6#

我偶然发现了这个问题好几次,最后我决定实现我的解决方案,不依赖于boost。对于引用类型,它禁用赋值运算符,不允许比较指针或r值。它基于一个类似的work,我以前做过,它使用nullptr而不是nullopt来表示值的缺失。出于这个原因,该类型被称为nullable和编译是禁用指针类型(他们有nullptr反正).请让我知道,如果你发现任何明显或任何不明显的问题与它.

#ifndef COMMON_NULLABLE_H
#define COMMON_NULLABLE_H
#pragma once

#include <cstddef>
#include <stdexcept>
#include <type_traits>

namespace COMMON_NAMESPACE
{
    class bad_nullable_access : public std::runtime_error
    {
    public:
        bad_nullable_access()
            : std::runtime_error("nullable object doesn't have a value") { }
    };

    /**
     * Alternative to std::optional that supports reference (but not pointer) types
     */
    template <typename T, typename = std::enable_if_t<!std::is_pointer<T>::value>>
    class nullable final
    {
    public:
        nullable()
            : m_hasValue(false), m_value{ } { }

        nullable(T value)
            : m_hasValue(true), m_value(std::move(value)) { }

        nullable(std::nullptr_t)
            : m_hasValue(false), m_value{ } { }

        nullable(const nullable& value) = default;

        nullable& operator=(const nullable& value) = default;

        nullable& operator=(T value)
        {
            m_hasValue = true;
            m_value = std::move(value);
            return *this;
        }

        nullable& operator=(std::nullptr_t)
        {
            m_hasValue = false;
            m_value = { };
            return *this;
        }

        const T& value() const
        {
            if (!m_hasValue)
                throw bad_nullable_access();

            return m_value;
        }

        T& value()
        {
            if (!m_hasValue)
                throw bad_nullable_access();

            return m_value;
        }

        bool has_value() const { return m_hasValue; }
        const T* operator->() const { return &m_value; }
        T* operator->() { return &m_value; }
        const T& operator*() const { return m_value; }
        T& operator*() { return m_value; }

    public:
        template <typename T2>
        friend bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs);

        template <typename T2>
        friend bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs);

        template <typename T2>
        friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);

        template <typename T2>
        friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);

        template <typename T2>
        friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);

        template <typename T2>
        friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);

        template <typename T2>
        friend bool operator==(const nullable<T2>& lhs, const T2& rhs);

        template <typename T2>
        friend bool operator==(const T2& lhs, const nullable<T2>& rhs);

        template <typename T2>
        friend bool operator==(const nullable<T2>& lhs, std::nullptr_t);

        template <typename T2>
        friend bool operator!=(const nullable<T2>& lhs, const T2& rhs);

        template <typename T2>
        friend bool operator!=(const T2& lhs, const nullable<T2>& rhs);

        template <typename T2>
        friend bool operator==(std::nullptr_t, const nullable<T2>& rhs);

        template <typename T2>
        friend bool operator!=(const nullable<T2>& lhs, std::nullptr_t);

        template <typename T2>
        friend bool operator!=(std::nullptr_t, const nullable<T2>& rhs);

    private:
        bool m_hasValue;
        T m_value;
    };

    // Template spacialization for references
    template <typename T>
    class nullable<T&> final
    {
    public:
        nullable()
            : m_hasValue(false), m_value{ } { }

        nullable(T& value)
            : m_hasValue(true), m_value(&value) { }

        nullable(std::nullptr_t)
            : m_hasValue(false), m_value{ } { }

        nullable(const nullable& value) = default;

        nullable& operator=(const nullable& value) = default;

        const T& value() const
        {
            if (!m_hasValue)
                throw bad_nullable_access();

            return *m_value;
        }

        T& value()
        {
            if (!m_hasValue)
                throw bad_nullable_access();

            return *m_value;
        }

        bool has_value() const { return m_hasValue; }
        const T* operator->() const { return m_value; }
        T* operator->() { return m_value; }
        const T& operator*() const { return *m_value; }
        T& operator*() { return *m_value; }

    public:
        template <typename T2>
        friend bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);

        template <typename T2>
        friend bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs);

        template <typename T2>
        friend bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);

        template <typename T2>
        friend bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs);

        template <typename T2>
        friend bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs);

        template <typename T2>
        friend bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs);

        template <typename T2>
        friend bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);

        template <typename T2>
        friend bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs);

        template <typename T2>
        friend bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);

        template <typename T2>
        friend bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs);

        template <typename T2>
        friend bool operator==(const nullable<T2>& lhs, std::nullptr_t);

        template <typename T2>
        friend bool operator==(std::nullptr_t, const nullable<T2>& rhs);

        template <typename T2>
        friend bool operator!=(const nullable<T2>& lhs, std::nullptr_t);

        template <typename T2>
        friend bool operator!=(std::nullptr_t, const nullable<T2>& rhs);

    private:
        bool m_hasValue;
        T* m_value;
    };

    template <typename T>
    using nullableref = nullable<T&>;

    template <typename T2>
    bool operator==(const nullable<T2>& lhs, const nullable<T2>& rhs)
    {
        if (lhs.m_hasValue != rhs.m_hasValue)
            return false;

        if (lhs.m_hasValue)
            return lhs.m_value == rhs.m_value;
        else
            return true;
    }

    template <typename T2>
    bool operator!=(const nullable<T2>& lhs, const nullable<T2>& rhs)
    {
        if (lhs.m_hasValue != rhs.m_hasValue)
            return true;

        if (lhs.m_hasValue)
            return lhs.m_value != rhs.m_value;
        else
            return false;
    }

    template <typename T2>
    bool operator==(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
    {
        if (lhs.m_hasValue != rhs.m_hasValue)
            return true;

        if (lhs.m_hasValue)
            return lhs.m_value != *rhs.m_value;
        else
            return false;
    }

    template <typename T2>
    bool operator!=(const nullable<std::decay_t<T2>>& lhs, const nullable<T2&>& rhs)
    {
        if (lhs.m_hasValue != rhs.m_hasValue)
            return true;

        if (lhs.m_hasValue)
            return lhs.m_value != *rhs.m_value;
        else
            return false;
    }

    template <typename T2>
    bool operator==(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
    {
        if (lhs.m_hasValue != rhs.m_hasValue)
            return false;

        if (lhs.m_hasValue)
            return *lhs.m_value == rhs.m_value;
        else
            return true;
    }

    template <typename T2>
    bool operator!=(const nullable<T2&>& lhs, const nullable<std::decay_t<T2>>& rhs)
    {
        if (lhs.m_hasValue != rhs.m_hasValue)
            return true;

        if (lhs.m_hasValue)
            return *lhs.m_value != rhs.m_value;
        else
            return false;
    }

    template <typename T2>
    bool operator==(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
    {
        if (lhs.m_hasValue != rhs.m_hasValue)
            return false;

        if (lhs.m_hasValue)
            return *lhs.m_value == *rhs.m_value;
        else
            return true;
    }

    template <typename T2>
    bool operator!=(const nullable<T2&>& lhs, const nullable<T2&>& rhs)
    {
        if (lhs.m_hasValue != rhs.m_hasValue)
            return true;

        if (lhs.m_hasValue)
            return *lhs.m_value != *rhs.m_value;
        else
            return false;
    }

    template <typename T2>
    bool operator==(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
    {
        if (!lhs.m_hasValue)
            return false;

        return *lhs.m_value == rhs;
    }

    template <typename T2>
    bool operator!=(const nullable<T2&>& lhs, const std::decay_t<T2>& rhs)
    {
        if (!lhs.m_hasValue)
            return true;

        return *lhs.m_value != rhs;
    }

    template <typename T2>
    bool operator==(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
    {
        if (!rhs.m_hasValue)
            return false;

        return lhs == *rhs.m_value;
    }

    template <typename T2>
    bool operator!=(const std::decay_t<T2>& lhs, const nullable<T2&>& rhs)
    {
        if (!rhs.m_hasValue)
            return true;

        return lhs != *rhs.m_value;
    }

    template <typename T2>
    bool operator==(const nullable<T2>& lhs, const T2& rhs)
    {
        if (!lhs.m_hasValue)
            return false;

        return lhs.m_value == rhs;
    }

    template <typename T2>
    bool operator!=(const nullable<T2>& lhs, const T2& rhs)
    {
        if (!lhs.m_hasValue)
            return true;

        return lhs.m_value != rhs;
    }

    template <typename T2>
    bool operator==(const T2& lhs, const nullable<T2>& rhs)
    {
        if (!rhs.m_hasValue)
            return false;

        return lhs == rhs.m_value;
    }

    template <typename T2>
    bool operator!=(const T2& lhs, const nullable<T2>& rhs)
    {
        if (!rhs.m_hasValue)
            return true;

        return lhs != rhs.m_value;
    }

    template <typename T2>
    bool operator==(const nullable<T2>& lhs, std::nullptr_t)
    {
        return !lhs.m_hasValue;
    }

    template <typename T2>
    bool operator!=(const nullable<T2>& lhs, std::nullptr_t)
    {
        return lhs.m_hasValue;
    }

    template <typename T2>
    bool operator==(std::nullptr_t, const nullable<T2>& rhs)
    {
        return !rhs.m_hasValue;
    }

    template <typename T2>
    bool operator!=(std::nullptr_t, const nullable<T2>& rhs)
    {
        return rhs.m_hasValue;
    }
}

#endif // COMMON_NULLABLE_H

字符串

相关问题