如何为 const 模板参数定义复制构造函数?
How to define a copy constructor for a const template parameter?
我正在创建自定义迭代器,但我无法满足创建 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 provided a solution but it does not satisfy all possible STL test cases. I'm testing @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;
您应该为您的 class
定义一个模板复制构造函数
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;
}
复制构造函数应该有一个 const 引用,此代码将编译
some_class(const some_class<U>& other){}
首先 - 你不能假设 std::string::const_iterator
只是常规迭代器的 "const version" - 像这样 const std::string::iterator
.
当您查看 STL 库实现时(这只是来自 gcc4.9.2 STL header for basic_string 的示例):
typedef __gnu_cxx::__normal_iterator<pointer, basic_string> iterator;
typedef __gnu_cxx::__normal_iterator<const_pointer, basic_string>
const_iterator;
如您所见 - 两个迭代器的不同之处在于 return 指针值 - pointer
与 const_pointer
- 就是这种情况 - "const iterator" 不是什么东西不能改变 - 但是 returns const pointer/references 所以你不能修改迭代器迭代的值。
所以 - 我们可以进一步调查并了解如何实现所需的从非 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 指针的实例。
我相信你在你的情况下也会这样做
- 有真正的 const_iterator - 不仅仅是常规迭代器的 const 版本
- 并且有来自 const_iterator 的模板构造函数,带有 enable_if 限制,禁止从任何东西构造(我的意思是迭代器超过整数,来自 std::strings 的迭代器)
按照你的例子:
#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>&) {
}
};
从这个例子中可以看出两点:
- 还需要赋值运算符
- 您只能启用从 non-const 到 const 版本 - 这是通过
enable_if
/remove_cv
的组合实现的(remove_const
也可以 - 但为什么不构建也是不稳定的版本——无论如何 cv
比 const
) 短
我正在创建自定义迭代器,但我无法满足创建 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 provided a solution but it does not satisfy all possible STL test cases. I'm testing @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;
您应该为您的 class
定义一个模板复制构造函数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;
}
复制构造函数应该有一个 const 引用,此代码将编译
some_class(const some_class<U>& other){}
首先 - 你不能假设 std::string::const_iterator
只是常规迭代器的 "const version" - 像这样 const std::string::iterator
.
当您查看 STL 库实现时(这只是来自 gcc4.9.2 STL header for basic_string 的示例):
typedef __gnu_cxx::__normal_iterator<pointer, basic_string> iterator;
typedef __gnu_cxx::__normal_iterator<const_pointer, basic_string>
const_iterator;
如您所见 - 两个迭代器的不同之处在于 return 指针值 - pointer
与 const_pointer
- 就是这种情况 - "const iterator" 不是什么东西不能改变 - 但是 returns const pointer/references 所以你不能修改迭代器迭代的值。
所以 - 我们可以进一步调查并了解如何实现所需的从非 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 指针的实例。
我相信你在你的情况下也会这样做
- 有真正的 const_iterator - 不仅仅是常规迭代器的 const 版本
- 并且有来自 const_iterator 的模板构造函数,带有 enable_if 限制,禁止从任何东西构造(我的意思是迭代器超过整数,来自 std::strings 的迭代器)
按照你的例子:
#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>&) {
}
};
从这个例子中可以看出两点:
- 还需要赋值运算符
- 您只能启用从 non-const 到 const 版本 - 这是通过
enable_if
/remove_cv
的组合实现的(remove_const
也可以 - 但为什么不构建也是不稳定的版本——无论如何cv
比const
) 短