0

考虑以下参考包装器:

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

我想知道:

  • 如何仅通过模板别名声明 const 引用包装器template <class T> using const_wrapper = /* const wrapper<T> or wrapper<const T>?*/
  • 如果在这种状态下不可能,如何更改包装器结构以使前面的点成为可能?
  • 如何解决以下问题:int i = 42; wrapper<const char> w(i);会编译但不工作(我想阻塞构造函数)
  • 对于什么具体问题,iterator一般const_iterator有两种不同的实现方式?
4

3 回答 3

2

如何通过仅模板别名模板声明 const 引用包装器<class T> using const_wrapper = /* const wrapper<T> or wrapper<const T>?*/

显然那将是:

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

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

但是请注意,如果是,set则无法调用您的函数。这是出于显而易见的原因;你不能改变一个值。Tconstconst

如何解决以下问题:int i = 42; wrapper<const char> w(i);会编译但不工作(我想阻塞构造函数)

这实际上有点复杂。如果用户尝试使用不完全匹配的类型,您需要做的是导致编译失败T。为此,您需要使用=deleteC++11 的特性:

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

当任何人传递的类型与T. 而且因为它是deleted,所以当人们尝试使用它时会出现编译器错误。

对于什么具体问题,iterator 和 const_iterator general 有两种不同的实现方式?

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

于 2015-12-04T16:39:08.393 回答
0
  • 如何仅通过模板别名声明 const 引用包装器template <class T> using const_wrapper = /* const wrapper<T> or wrapper<const T>?*/

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

  • 如果在这种状态下不可能,如何更改包装器结构以使前面的点成为可能?

看上面。

  • 如何解决以下问题:int i = 42; wrapper<const char> w(i);会编译但不工作(我想阻塞构造函数)

添加部分专业化

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;
};
  • 对于什么具体问题,iterator一般const_iterator有两种不同的实现方式?

通常应该可以隐式转换iteratorconst_iterator但不能反过来。所以你要么需要一个只存在于const_iterator表单的 SFINAEd 构造函数,要么你有两个实现,一个支持额外的转换,一个不支持。

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

于 2015-12-08T17:05:02.833 回答
-1

假设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
}

对于你的最后一个问题,我不能给你一个满意的答案。这几乎是一个实现细节。我想不出任何一般情况下iterator并且const_iterator至少没有相互继承——非 const 版本扩展了 const 版本。

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

于 2015-12-04T16:42:22.707 回答