std::iterator 的错误指示器
Error indicator for std::iterator
我需要用 std::string::npos
代替 std::iterator
,这与 "next position after last valid address" (end
).
无关
这是一些背景:
我有一个方法,我需要在两个迭代器和 return 迭代器之间找到某个值到它旁边的位置(可能是也可能不是 end
)。方法调用可能只是一个虚拟对象,不需要任何操作——在这种情况下,我们只是 return begin
。我还需要一些指示,表明我们无法找到有问题的值,这就是问题的症结所在:我不知道如何指示它。
这是一个简单的例子:
class FinderClass
{
public:
FinderClass(uint8_t* byte): byte_(byte) {}
std::iterator findEndOfFrame(std::iterator begin, std::iterator end)
{
if (byte_ == NULL)
{
return byte_;
}
std::iterator result = std::find(begin, end, *byte_);
if (result != end)
{
return ++result;
}
return ???;
}
private:
uint8_t* byte_;
}
我们可以这样使用它:
std::vector<uint8_t> myVector;
myVector.push_back(1);
myVector.push_back(2);
myVector.push_back(3);
uint8_t val = 2;
FinderClass finder(&val);
std::iterator result = finder.findEndOfFrame(myVector.begin(), myVector.end());
if (/* no error */)
{
size_t len = std::distance(myVector.begin(), result);
}
在这种情况下,len
的预期值为 2。如果 val
的值设置为 3
,预期结果将是 3,如果是 4 - 错误。 0
只有在构造函数中提供 NULL 时才是合适的结果。
请记住,这是更复杂架构的一个非常简化的版本。实际上,我不能简单地检查构造函数中提供的内容,因为它被多个抽象层分隔开,并且真正的 FinderClass
实现了一个接口(这是相当奇怪的实现,因为在其他实现中没有 NULL 选项byte_
的值)。在我们调用 std::distance
的地方只有指向 superclass 的指针可用。
我也不使用 std::iterator
本身,而是使用派生的 class,但是由于 std::iterator
没有定义某种 npos
值,我认为可能有一个很好的理由,所以我自己也没有这样做。为简单起见,我在此示例中仅使用了 std::iterator
。
所以总结:如果end
不能用来指示错误,应该用什么代替?
几个注意事项:
- 团队的编码标准禁止例外
- 接口中的更改(例如将 returned 值更改为指向
std::iterator
的指针,并在出错时简单地 return NULL)被视为“核选项,因为它需要更新许多其他实现及其单元测试。
- C++11 不可用
鉴于限制,我会 return 一个 boost::optional<Iterator>
或指向下一个元素的指针(在这种情况下,如果没有,你可以 return NULL
下一个元素)。
optional
当然更具表现力和安全性,并且需要更少的不公正划分将结果转换回迭代器。
使用迭代器的代码通常(并且可以说应该)几乎不知道迭代器的实际类型,而只是期望具有大致递增和取消引用运算符的东西。您没有指定迭代器是否必须是特定类型,如果不是,那么 return 一个实际向量迭代器的包装器相对容易,它本身实现迭代器行为以及具有无效的额外功能状态。正如在其他答案中所说,如果可能的话,选择 optional<> 可能是一个更好的选择,因为它清楚地表达了您的实际意思。不管怎样,伪代码:
//template argument is the actual iterator type
template< class Iter >
class iterator : std::iterator< Iter::iterator_category,
Iter::value_type, .... >
{
public:
enum tag { invalid_tag };
//construct the invalid 'error' iterator
iterator( invalid_tag ) :
error( true )
{}
//construct actual iterator
iterator( Iter iter ) :
error( false ),
iter( iter )
{}
//forward all functions
reference operator * ()
{
assert( valid() );
return *iter;
}
....
//check validity
bool valid() const
{
return !error;
}
private:
bool error;
Iter iter;
};
我需要用 std::string::npos
代替 std::iterator
,这与 "next position after last valid address" (end
).
这是一些背景:
我有一个方法,我需要在两个迭代器和 return 迭代器之间找到某个值到它旁边的位置(可能是也可能不是 end
)。方法调用可能只是一个虚拟对象,不需要任何操作——在这种情况下,我们只是 return begin
。我还需要一些指示,表明我们无法找到有问题的值,这就是问题的症结所在:我不知道如何指示它。
这是一个简单的例子:
class FinderClass
{
public:
FinderClass(uint8_t* byte): byte_(byte) {}
std::iterator findEndOfFrame(std::iterator begin, std::iterator end)
{
if (byte_ == NULL)
{
return byte_;
}
std::iterator result = std::find(begin, end, *byte_);
if (result != end)
{
return ++result;
}
return ???;
}
private:
uint8_t* byte_;
}
我们可以这样使用它:
std::vector<uint8_t> myVector;
myVector.push_back(1);
myVector.push_back(2);
myVector.push_back(3);
uint8_t val = 2;
FinderClass finder(&val);
std::iterator result = finder.findEndOfFrame(myVector.begin(), myVector.end());
if (/* no error */)
{
size_t len = std::distance(myVector.begin(), result);
}
在这种情况下,len
的预期值为 2。如果 val
的值设置为 3
,预期结果将是 3,如果是 4 - 错误。 0
只有在构造函数中提供 NULL 时才是合适的结果。
请记住,这是更复杂架构的一个非常简化的版本。实际上,我不能简单地检查构造函数中提供的内容,因为它被多个抽象层分隔开,并且真正的 FinderClass
实现了一个接口(这是相当奇怪的实现,因为在其他实现中没有 NULL 选项byte_
的值)。在我们调用 std::distance
的地方只有指向 superclass 的指针可用。
我也不使用 std::iterator
本身,而是使用派生的 class,但是由于 std::iterator
没有定义某种 npos
值,我认为可能有一个很好的理由,所以我自己也没有这样做。为简单起见,我在此示例中仅使用了 std::iterator
。
所以总结:如果end
不能用来指示错误,应该用什么代替?
几个注意事项:
- 团队的编码标准禁止例外
- 接口中的更改(例如将 returned 值更改为指向
std::iterator
的指针,并在出错时简单地 return NULL)被视为“核选项,因为它需要更新许多其他实现及其单元测试。 - C++11 不可用
鉴于限制,我会 return 一个 boost::optional<Iterator>
或指向下一个元素的指针(在这种情况下,如果没有,你可以 return NULL
下一个元素)。
optional
当然更具表现力和安全性,并且需要更少的不公正划分将结果转换回迭代器。
使用迭代器的代码通常(并且可以说应该)几乎不知道迭代器的实际类型,而只是期望具有大致递增和取消引用运算符的东西。您没有指定迭代器是否必须是特定类型,如果不是,那么 return 一个实际向量迭代器的包装器相对容易,它本身实现迭代器行为以及具有无效的额外功能状态。正如在其他答案中所说,如果可能的话,选择 optional<> 可能是一个更好的选择,因为它清楚地表达了您的实际意思。不管怎样,伪代码:
//template argument is the actual iterator type
template< class Iter >
class iterator : std::iterator< Iter::iterator_category,
Iter::value_type, .... >
{
public:
enum tag { invalid_tag };
//construct the invalid 'error' iterator
iterator( invalid_tag ) :
error( true )
{}
//construct actual iterator
iterator( Iter iter ) :
error( false ),
iter( iter )
{}
//forward all functions
reference operator * ()
{
assert( valid() );
return *iter;
}
....
//check validity
bool valid() const
{
return !error;
}
private:
bool error;
Iter iter;
};