const auto和对象常量性

3
想象一些函数(RetrieveResult),通过指针/引用/值返回对象。我不知道也不想知道具体的方式,因为这些可能会改变。我只想使用auto存储结果,并在当前作用域内保护该对象免受意外更改,例如如果该对象被向上传播。
写下以下代码是相当直观的:
const auto result = RetrieveResult();

如果RetrieveResult通过值或引用返回一个对象,则一切正常。但如果该函数返回指针,则const修饰的是该指针而不是指针所指向的对象。那么我仍然可以修改该对象。因此,需要写成:

const auto const result = ....

在编译时会导致以下错误:

重复的“const”

当然,我可以像这样声明变量: const auto* ... const auto* const...

但是这种方式让我与指针密切相关,即它不是一种通用的解决方案。

是否有可能保持真正的常量性,并同时提供灵活性(独立于具体类型)呢?


“我不知道,也不想知道。” 如果你不知道对象是什么,那么你该如何访问它呢?你可能认为它是一个指针,但实际上它可能是一个重载了 -> 的类。 :P - Rakete1111
@Rakete1111,例如,我可能只是转发对象,而不对其进行修改。 - Alexey
2
@Alexey 如果你转发它,为什么还在意它所指向的指针是“const”还是不是? - Rakete1111
在VS2015中,const auto const不会产生任何编译器错误,只会产生一个警告:warning C4114: same type qualifier used more than once - alexeykuzmin0
@Rakete1111,总会有一些理由的) C++ 语法非常强大,我正在努力理解所有这些魔鬼般的事情) - Alexey
2个回答

4

现在有一个叫做std::propagate_const的实用工具,在库基础 v2 中提供了试验性支持。您可以在其上编写一个类型特征,为您执行此操作(如果您没有std::propagate_const,可以考虑自行编写它 :))

namespace {
    template <typename T, typename = std::enable_if_t<true>>
    PropagateConst {
        using type = T;
    };
    template <typename T>
    PropagateConst<T, std::enable_if_t<std::is_same<
            decltype(*std::declval<std::decay_t<T>>()), 
            decltype(*std::declval<std::decay_t<T>>())>::value>> {
        using type = std::propagate_const_t<std::decay_t<T>>;
    };

    template <typename T>
    using PropagateConst_t = typename PropagateConst<T>::type;

    template <typename Type>
    decltype(auto) propagate_const(Type&& in) {
        return PropagateConst_t<std::add_rvalue_reference_t<Type>>{in};
    }
} // <anonymous>

// then use it like this
const auto result = propagate_const(RetrieveResult());

请注意,我上面的解决方案仅检查可能指针类型中是否存在一个 operator*。您可能需要考虑编写更广泛的测试来进行检测。
此外,请注意,在 propagate_const 示例中使用了引用折叠,因此在某些情况下,您可能期望省略移动,但实际上至少会发生一次移动。您可以根据自己的使用情况对其进行优化。我只是想概述一下我的想法。也许这会有所帮助。

1
template<class T>
struct very_const_t { using type=T; };
template<class T>
struct very_const_t<T*> { using type=typename very_const_t<T>::type const*; };
template<class T>
struct very_const_t<T&> { using type=typename very_const_t<T>::type const&; };

template<class T>
typename very_const_t<T>::type&&
very_const( T&& t ) { return std::forward<T>(t); }

然后:
const auto result = very_const(RetrieveResult());

请注意,这可能会阻止省略。但是,我仔细地没有阻止移动语义。
这不会将常量移动到智能指针中。如果您想要这样做:
template<class T, class D>
struct very_const_t<std::unique_ptr<T,D>> { using type=std::unique_ptr<typename very_const_t<T>::type const, D>; };

template<class T>
struct very_const_t<std::shared_ptr<T>> { using type=std::shared_ptr<typename very_const_t<T>::type const>; };

将为 uniqueshared 进行操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接