c++ - 在模板函数中自动将指针/迭代器转换为 const

标签 c++ templates pointers iterator constants

我正在尝试创建一个既适用于指针又适用于迭代器的函数(我希望在测试期间让迭代器对集合进行检查,并灵活地使用数组来公开 C 库而无需包装)。我想使用尽可能少的样板代码来做到这一点,而不依赖于 Boost 或 C++11 功能。好的,我没有,我的需求规范有。

到目前为止,解决方案非常简单:在迭代器上参数化的函数模板。问题是我希望迭代器引用 const 值;部分是为了避免愚蠢的错误(改变一些严格来说是输入值的东西),部分是为了允许编译器使用常量来进行优化(这些值在紧密循环中使用,每个优化都是受欢迎的。

考虑这个例子:

void foo(vector<int>::const_iterator it)
{
        //it[5] = 7; // 1
        int tmp = it[5];
}

void bar(const int *it)
{
        //it[5] = 7; // 2
        int tmp = it[5];
}

template<class T>
void baz(T it)
{
        it[5] = 7; // 3
        int tmp = it[5];
}

template<class T>
void qux(T it)
{
        //it[5] = 7; // 4
        int tmp = it[5];
}

template<class T>
void quux(const T it)
{
        it[5] = 7; // 5
        int tmp = it[5];
}

vector<int> a(10);
const vector<int> b(10);
int c[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
const int d[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

foo(a.begin());
foo(b.begin());
bar(c);
bar(d);
baz(a.begin());
//baz(b.begin()); // 5
baz(c);
//baz(d); // 6
qux(a.begin());
qux(b.begin());
qux(c);
qux(d);
quux(a.begin()); // 7
//quux(b.begin()); // 8
quux(c);
//quux(d); // 9

Foo 和 bar 按预期工作;我可以读取值,如果我尝试重新注释第 1 行或第 2 行,则会出现编译错误。此外,我可以使用 const 和非常量值调用函数。不过,这些需要重复代码。

Baz 使用简单的模板避免了这种情况。我可以使用指针和迭代器调用它。但是,如果我从不使用 const 值调用它(ll.5 和 6 被注释掉),我可以分配给指向的值(l.3 不会产生错误)。删除分配 (qux, l.4) 允许我传递 const 和非成本值。这在原则上很好(我可以使用测试用例捕获错误),但它不会让编译器知道该值应被视为 const 并相应地应用优化。在这种情况下,我希望调用者不必担心 constness(特别是因为忘记 constness 不是错误,只会“莫名其妙地”产生较慢的代码)。

现在,我想要像 quux 这样的东西,除了,你知道,工作。我想要一些行为类似于 foo 和 bar 的东西,但没有代码重复。问题是我得到一个指向非常量整数的 const 指针/迭代器,而不是指向 const 整数的指针/迭代器(无论是否为 const)。我可以在第 5 行或(第 8 和第 9 行)离开,但不能同时离开。我希望编译器强制执行,如果我留在第 7-9 行中的任何一行,第 5 行是编译错误。

我知道,我可以使用模板特化和/或函数重载(如下)来解决这个问题,但这需要我这样做(如果我忘记了,我可能会在 baz 情况下结束,除非没有测试用例来找出错误)。代码应该易于移动、临时复制以供替代实现等,但仍然可以提高效率和健壮性。这也迫使我更多地了解我的收藏。

template<class T>
void quux_(T it)
{
        it[5] = 7; // 5
        int tmp = it[5];
}

void quux(const int* it) { quux_<const int*>(it); }
void quux(std::vector<int>::const_iterator it) { quux_<std::vector<int>::const_iterator>(it); }

我也可以通过显式转换为相应的 const 值来解决这个问题(尽管如果您不知道集合,将迭代器转换为相应的 const_iterator 似乎有点麻烦,而且 C++ 甚至不太满意参数化集合,尽管我认为可以修复)。不过,这确实强加了很多样板(我有多达 4 个这样的参数和几十个这样的函数)。在这两种选择中,这是我的首选,尽管我想要一些像 foo 和 bar 一样简单的东西。

// template<class U> struct constify<typename std::vector<U>::iterator> { typedef typename std::vector<U>::const_iterator type; };
template<> struct constify<typename std::vector<int>::iterator> { typedef typename std::vector<int>::const_iterator type; };
template<> struct constify<std::vector<int>::const_iterator> { typedef std::vector<int>::const_iterator type; };
template<class U> struct constify<U*> { typedef const U* type; };
template<class U> struct constify<const U*> { typedef const U* type; };

template<class T>
void quux(T p_it)
{
        typename constify<T>::type it = p_it;
        //it[5] = 7; // 5
        int tmp = it[5];
}

有没有人有一种优雅的方法来实现这一点?如果唯一的解决方案是显式转换参数,我还希望在更优雅的 constify 版本上输入——如果它只能处理 vector 迭代器和指针,我会很满意,但它最好在集合基类型中是通用的.

最佳答案

不幸的是,除了模板特化之外,没有其他方法可以实现您想要做的事情。

对我来说,一个干净的解决方案是:

//const version
template<class T>
void quux(const T it)
{ }

//non const version
template<class T>
void quux( T it)
{ }

在我看来,这不是代码重复,这两个接口(interface)是不同的,您应该只向客户提供他需要的最低限度。

当您尝试开始将两者混合时,只会导致问题。 如果您需要修改函数内部的参数,只给出非 const 版本,客户端将能够毫无问题地使用 const 参数调用您。 (非 const 到 const 转换开箱即用)

在这种情况下,您可以确保您没有修改 quux 函数内的参数,只需提供 const 参数即可。

现在,const_casting 参数来在你的函数中修改它只是一个大罪:-)

tldr; 考虑您的界面公开的内容,并编写最低限度的代码。

关于c++ - 在模板函数中自动将指针/迭代器转换为 const,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21886659/

相关文章:

c++ - 模板隐式实例化和内联成员

指向从未成为 const 的 const 对象的 C++ 指针

c - 在转换为 int ** 后取消引用指针如何更改它指向的值?

html - 如何在 HTML 中创建模板?

c - 何时使用指针的高级规划?

c++ - mktime 仅处理 Clang 上的闰年?

c++ - 为什么是 std::bitset::reference::operator~?

c++ - 使用 qt 进行 SSL 握手时出错

c++ - 如何在 C++ 中拆分 x 和 y 坐标

c++ - 根据最大可能数在编译时确定索引类型