如何为const模板参数定义复制构造函数?

时间:2016-02-10 07:28:22

标签: c++ stl const std copy-constructor

我正在创建一个自定义迭代器,但我无法满足我创建const迭代器并使用非const begin()初始化它的情况。根据STL,这是合法的,可以使用std :: string:

进行演示
#include <string>

using namespace std;

int main() {
    string::iterator a;
    string::const_iterator b = a;

    return 0;
}

我无法弄清楚如何使其发挥作用:

template<typename T>
class some_class {
};

int main() {
    some_class<int> a;

    // Works OK!
    const some_class<int> const_b = a;

    // error: conversion from 'some_class<int>' to non-scalar type 'const some_class<const int>'
    const some_class<const int> const_c = a;

    return 0;
}

更新

@ALEXANDER KONSTANTINOV提供了一个解决方案,但它不能满足所有可能的STL测试用例。我接下来正在测试@Bo Persson的建议。将构造函数更改为const some_class<U>& other将允许它编译,但iterator a = const_iterator b也将错误地为真。

#include <string>

using namespace std;

template<typename T>
class some_class {
public:
    some_class() {
    }

    template<typename U>
    some_class(some_class<U>& other) {
    }
};

namespace name {
    typedef some_class<int> iterator;
    typedef const some_class<const int> const_iterator;
}

int main() {
    string::iterator a;
    string::const_iterator b = a;
    const string::iterator c;
    string::iterator d = c;
    string::const_iterator e = c;

    name::iterator f;
    name::const_iterator g = f;
    const name::iterator h;
    name::iterator i = h;
    name::const_iterator j = h; // <- Error

    return 0;
}

更新

关于向构造函数添加const似乎存在一些混淆。这是一个测试用例:

// This is not allowed by the STL
//string::const_iterator _a;
//string::iterator _b = _a; // <- Error!

// This should NOT compile!
name::const_iterator _a;
name::iterator _b = _a;

3 个答案:

答案 0 :(得分:2)

首先 - 你不能假设std::string::const_iterator只是&#34; const版本&#34;常规迭代器 - 就像这个const std::string::iterator

当你查看你的STL库实现时(这只是来自base_string的gcc4.9.2 STL头的例子):

  typedef __gnu_cxx::__normal_iterator<pointer, basic_string>  iterator;
  typedef __gnu_cxx::__normal_iterator<const_pointer, basic_string>
                                                        const_iterator;

正如您所看到的 - 迭代器的不同之处在于返回指针值 - pointer vs const_pointer - 就是这种情况 - &#34; const iterator&#34;不是不能改变的东西 - 但是返回const指针/引用的东西,所以你不能修改迭代器迭代的值。

所以 - 我们可以进一步研究,看看如何实现从非const到const版本的所需复制:

  // Allow iterator to const_iterator conversion
  template<typename _Iter>
    __normal_iterator(const __normal_iterator<_Iter,
          typename __enable_if<
           (std::__are_same<_Iter, typename _Container::pointer>::__value),
          _Container>::__type>& __i) _GLIBCXX_NOEXCEPT
    : _M_current(__i.base()) { }

所以,基本上 - 这个构造函数接受同一模板的任何实例(__normal_iterator) - 但是它有enable_if闭包只允许const指针的实例。

我相信你会在你的情况下做同样的事情

  1. 有真正的const_iterator - 而不仅仅是常规迭代器的常量版本
  2. 并且使用const_iterator中的模板构造函数,使用enable_if限制来禁止从任何东西构造(我的意思是iterator over iterator from iterator over std :: strings)
  3. 根据你的例子:

    #include <type_traits>
    
    template<typename T>
    class some_class {
    public:
        some_class() {
        }
    
        template <typename U>
        using allowed_conversion_from_non_const_version = std::enable_if_t<std::is_same<std::remove_cv_t<T>,U>::value>;
    
        template<typename U, typename EnableIf = allowed_conversion_from_non_const_version<U>>
        some_class(const some_class<U>&) {
        }
    
        template<typename U, typename EnableIf = allowed_conversion_from_non_const_version<U>>
        some_class& operator = (const some_class<U>&) {
        }
    };
    

    从这个例子中可以读到两件事:

    1. 还需要分配运算符
    2. 您只能从非const到const版本启用 - 这是通过组合enable_if / remove_cvremove_const也可以实现 - 但为什么不构造易失性版本 - 无论如何cv短于const

答案 1 :(得分:0)

您应该为您的班级定义模板复制构造函数

template<typename T>
class some_class {

public:
    template<typename U> friend class some_class;

    some_class()
    {
    }

    template<typename U>
    some_class(const some_class<U> &other)
    : data(other.data)
    {}

private:
    T* data;

};

int main() {
    some_class<int> a;

    // Works OK!
    const some_class<int> const_b = a;

    // error: conversion from 'some_class<int>' to non-scalar type 'const some_class<const int>'
    const some_class<const int> const_c = a;

    return 0;
}

答案 2 :(得分:-1)

复制构造函数应该有一个const引用,这段代码将编译

some_class(const some_class<U>& other){}