来自引用包装器的 Const 引用包装器

Const reference wrapper from reference wrapper

考虑以下参考包装器:

template <class T>
struct wrapper
{
    wrapper(T& x): reference{x} {}
    void set(const T& x) {reference = x;}
    T& get() const {return reference;}
    T& reference;
};

我在想:

How to declare a const reference wrapper through a template alias only template <class T> using const_wrapper = /* const wrapper<T> or wrapper<const T>?*/

显然是:

template <class T> using const_wrapper = wrapper<const T>;

包含的类型是 const,不是包装器。

但是请注意,如果 Tconst,则无法调用您的 set 函数。这是出于显而易见的原因;您无法更改 const 值。

How to solve the following problem: int i = 42; wrapper<const char> w(i); will compile but not work (I would like to block the constructor)

这个其实有点复杂。如果用户尝试使用与 T 不完全匹配的类型,您要做的就是导致编译失败。为此,您需要使用 C++11 的 =delete 功能:

template <class T>
struct wrapper
{
    wrapper(T& x): reference{x} {}
    template<typename U>
        wrapper(const U &) = delete;
    //...
};

当任何人传递的类型与 T 不完全匹配时,将使用第二个构造函数。由于它是 deleted,因此当人们尝试使用它时会出现编译器错误。

For what exact problem, iterator and const_iterator general have two different implementations?

谁说的?它们甚至不需要是 不同的 类型(例如考虑 set 的迭代器),更不用说需要有不同的 实现 .它们只是不同的类型别名。

假设 set 应该更改引用对象的值而不是引用本身:

template <class T>
struct wrapper
{
    wrapper(T& x) : reference{ x } {}

    template<typename U> 
    wrapper(U& x) : reference{ x } {} //<- Possible answer for question 3

    //template<typename U>
    //wrapper(U& x) = delete; //<- Possible answer for question 3

    void set(const T& x) const { reference = x; } //<- Answer for question 2
    T& get() const { return reference; }

    T& reference;
};

template <class T>
using const_wrapper = const wrapper<T>; //<- Answer for question 1


int main()
{
    //Testing const_wrapper
    char a;
    const_wrapper<char> b(a);
    b.set('a');

    //Testing narrowing cast
    int i = 42; 
    wrapper<const char> w(i);   //error C2397: conversion from 'int' to 'const char' requires a narrowing conversion
                                //error C2280 : 'wrapper<const char>::wrapper<int>(U &)' : attempting to reference a deleted function
    //w.set('b'); //error C3490: 'reference' cannot be modified because it is being accessed through a const object
}

关于你最后一个问题,我无法给你一个满意的答案。这几乎是一个实现细节。 我想不出 iteratorconst_iterator 至少没有相互继承的任何一般情况 - 非 const 版本扩展了 const 版本。

更新: 最后一部分可能是 MSVC 特定的实现细节。我仍然认为从 const_iterator 继承 iterator 是合理的,但应该指出这一点。

  • How to declare a const reference wrapper through a template alias only template <class T> using const_wrapper = /* const wrapper<T> or wrapper<const T>?*/

这取决于你想要表达的意思,你没有说清楚。

  • How to change the wrapper struct to make the preceding point possible if it is not possible in this state?

见上文。

  • How to solve the following problem: int i = 42; wrapper<const char> w(i); will compile but not work (I would like to block the constructor)

添加部分专业化

template <class T>
struct wrapper<const T>
{
    wrapper(const T& x): reference{x} {}
    wrapper(T&) = delete;
    void set(const T& x) {reference = x;}
    const T& get() const {return reference;}
    const T& reference;
};

或者向主模板添加适当约束的构造函数:

template <class T>
struct wrapper
{
    wrapper(T& x): reference{x} {}
    template<typename U, typename = std::enable_if_t<std::is_const<T>{} && std::is_same<T, const U>{}, void>>
    wrapper(U&) = delete;
    void set(const T& x) {reference = x;}
    T& get() const {return reference;}
    T& reference;
};
  • For what exact problem, iterator and const_iterator general have two different implementations?

通常应该可以将 iterator 隐式转换为 const_iterator 但反之则不行。因此,您要么需要一个仅适用于 const_iterator 形式的 SFINAEd 构造函数,要么您有两种实现,一种支持额外转换,另一种不支持。

使用 SFINAE 在 C++98 中不是一个选项,因此大多数标准库实现(在 Expression SFINAE 可用之前很久就编写了)使用两个单独的实现。