排序容器的隐式转换为显式布尔类型?

11

我正在尝试使用新的explicit转换运算符。如果你像这样写:

struct Data {
    explicit operator string(); 
};

无法意外地将Data转换为string。 目标数据类型bool是一个例外:在某些情况下,即使标记为explicit,也允许隐式转换——上下文转换。 因此,您可以在例如if(...)中使用这些数据类型:

struct Ok {
    explicit operator bool(); // allowed in if(...) anyway
};

这段话中的"25.4.(2) Sorting and related operations"似乎也允许像set这样的标准容器使用Compare函数对象。但我尝试使用gcc-4.7.0时失败了,我不确定是我的误解还是gcc中的一个错误。

#include <set>

struct YesNo { // Return value type of Comperator
    int val_;
    explicit YesNo(int y) : val_{y} {}
    /* explicit */ operator bool() { return val_!=0; }
};

static const YesNo yes{1};
static const YesNo no{0};

struct LessYesNo {  // Comperator with special return values
    YesNo operator()(int a, int b) const {
        return a<b ? yes : no;
    }
};

int main() {
    std::set<int,LessYesNo> data {2,3,4,1,2};
}

operator bool()之前没有加上explicit,示例也可以编译。根据我对“25.4.(2)”的理解,这应该也可以使用explicit我的理解是,对于set,显式的bool转换也应该起作用吗?这可能是gcc中的一个错误,还是我理解错了什么?


2
就标准而言,我同意你的解读。你在gcc中遇到了什么错误? - Alan Stokes
1
我同意它似乎是这样解释的,所以它可能尚未在gcc中实现(当编译器仍在编写时,我对谈论错误持谨慎态度)。 - Matthieu M.
2个回答

3
我的标准解读与您略有不同- 第25.4节涉及排序算法,而不是已排序容器;在25.4.(1)中确立的上下文意味着在25.4中指定的比较对象属性适用于算法,而不适用于已排序容器。
25.4中的所有操作都有两个版本:一个接受类型为Compare的函数对象,另一个使用运算符。 Compare是一个函数对象类型(20.8)。将应用于Compare类型对象的函数调用操作的返回值,在上下文转换为bool(4)时,如果调用的第一个参数小于第二个参数,则返回true,否则返回false。对于假设存在排序关系的算法,始终使用Compare comp。假定comp不会通过解引用的迭代器应用任何非常量函数。
我不知道您的示例是否有效,但我认为第25.4节在这里不适用。
使用vector和std::sort进行快速测试可行:
#include <list>
#include <algorithm>

struct YesNo { // Return value type of Comperator
    int val_;
    explicit YesNo(int y) : val_{y} {}
    explicit operator bool() { return val_!=0; }
};

static const YesNo yes{1};
static const YesNo no{0};

struct LessYesNo {  // Comperator with special return values
    YesNo operator()(int a, int b) const {
        return a<b ? yes : no;
    }
};

int main() {
    std::vector<int> data {2,3,4,1,2};
    std::sort(std::begin(data), std::end(data), LessYesNo());
}

编辑:

关联容器的比较参数是根据第25.4节定义的:

1 关联容器基于键提供快速检索数据。库提供了四种基本类型的关联容器:set、multiset、map和multimap。

2 每个关联容器都是在Key和一个排序关系Compare上进行参数化的,该排序关系在Key的元素上引入了严格的弱序(25.4)。此外,map和multimap将任意类型T与Key相关联。类型为Compare的对象被称为容器的比较对象。

就我所见,第23条没有对Compare类型的其他条件,因此假设满足25.4约束条件的类型同样适用,这似乎是合理的。


感谢提供有效的示例。我希望这对于容器也是适用的,否则可能需要进行 DR 处理。 - Matthieu M.
是的,我也希望如此 - 我正在浏览第23节,看看它对比较参数有什么说法。还要感谢您的点赞,那是我在stackoverflow上的第一篇帖子 :) - je4d
每个关联式容器都是基于键(Key)和一个排序关系Compare参数化的,该关系在Key元素上引入了一个严格弱序(25.4)。 - Dennis Zickefoose
@je4d:太完美了!既然你指出来了,我真不知道自己怎么会读错。我猜可能是我的大脑超前于自己,使用了隐含的知识,即集合和映射是被排序保持的——这当然是与排序不同的事情。谢谢。 - towi
@towi 感谢我的第一个采纳 :) 但请注意底部的编辑和Dennis Zickefoose的评论,根据§23.2.4.(2)的暗示,排序容器的比较器类型应用相同的约束条件,因此考虑到这一点,我认为这是libstdc++v3中的一个错误。 - je4d

2

我是否正确理解了标准,对于集合,也应该支持显式的布尔转换?

这有点是规范的灰色地带。比较函数的返回值需要“可转换为布尔值”。但在 explicit operator bool() 的情况下,这意味着什么是不清楚的。

例如,可以将 std::set 的比较用法写成如下形式:

CompFunc functor;
if(functor(input, currVal))
  ...

或者,你可以这样做:
CompFunc functor;
bool test = functor(input, currVal);
if(test)
  ...

这两个在C++11下是否合法?我不知道。显然,如果operator bool()explicit的话,第二个会失败。
我查看了std::shared_ptr的定义,发现它也有一个explicit operator bool()。在20.7.2.2节第2段中,还说std::shared_ptr可以“转换为bool”。
因此,我猜测第二个版本应该按以下方式实现:
CompFunc functor;
bool test = static_cast<bool>(functor(input, currVal));
if(test)
  ...

事实上,规范中没有明确说明这一点,这意味着应该将其作为缺陷报告进行归档。但是,它也应该被归档为GCC / libstdc ++错误。
个人而言,为了安全起见,我不会依赖它。

关于上下文转换

第4节第3段规定如下:

在这种情况下出现的表达式e被称为上下文转换为bool,并且当且仅当声明bool t(e)对于某些虚构的临时变量t是良好形式时,它是良好形式的。

因此,“上下文转换为bool”的操作意味着explicit operator bool()将起作用。由于std::set的“比较”函数必须符合25.4的要求,而这些要求包括“上下文转换为bool”,因此看起来像是GCC / libstdc ++的一个错误。
尽管如此,我仍然建议在能够避免时不要使用它。

引用中的关键部分是“当上下文转换为bool(4)时”。我认为标准中的“上下文”特别是为了抵消对boolexplicit转换。 - Matthieu M.
@MatthieuM.:请看我的补充。 - Nicol Bolas
你认为"std::shared_ptr可转换为bool"是否指的是"contextual conversion"吗?或者这段代码shared_ptr<X> x ... ; bool b = (bool)x; 也可以被称为"convertible to bool"吗? - towi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接