从 Visual Studio 2010 迁移到 2012 时C++11 问题



我正在尝试将我的项目从Visual Studio 2010移植到Visual Studio 2012。在我的代码中,我有一些文件处理,如下所示:

auto fileDeleter = [](FILE* f) { fclose(f); };
unique_ptr<FILE, decltype(fileDeleter)> fMinute(
fopen(minuteLogName.c_str(), "w"), fileDeleter);

unique_ptr<FILE, decltype(fileDeleter)> fIndividual(
fopen(individualLogName.c_str(), "w"), fileDeleter);
if (!fMinute || !fIndividual) {
throw Exceptions::IOException("One of the log files failed to open",
__FUNCTION__);
}

这在 2010 年没有任何问题,但在 2012 年它因条件而失败:

错误 C2678:二进制"!":找不到采用类型为> 'std::unique_ptr<_Ty,_Dx>' 的左侧操作数的运算符(或者没有可接受的转换)
...
可能是"内置C++运算符!(布尔)'

C++11 标准指定unique_ptr有一个布尔运算符,允许您像上面一样进行快速检查。更奇怪的是,VS2012 的unique_ptr定义有这样一个运算符:

_OPERATOR_BOOL() const _NOEXCEPT
{   // test for non-null pointer
return (this->_Myptr != pointer() ? _CONVERTIBLE_TO_TRUE : 0);
}

但是我在编译时遇到该错误。为什么?

是的,我可以改用ofstream,但这不是重点。

为了建立在 BigBoss 所说的基础上,C++11 要求std::unique_ptr使用explicit operator bool() noexcept,这解决了整个隐式转换为布尔的问题。除了。。。VC2012 尚不支持explicit运算符。因此,他们必须使用安全布尔成语。

虽然 safe-bool 习语很好,但它可能有缺陷(这就是explicit operator bool()存在的原因),具体取决于您如何实现该习语。你显然在VC2012中遇到了其中一个。使用!(fMinute && fIndividual)重新配置测试应该可以解决它。

但无论哪种方式,这都是Visual Studio的错误。由于行为发生了变化,即使您确实设法找到了解决方法,也应该提交错误报告。

您可以使用if (!(fMinute && fIndividual) )而不是if (!fMinute || !fIndividual)。C++说它们可以转换为布尔值,但operator bool通常会产生问题,例如您可能有一个接受int的函数,如果您的类有operator bool那么它可以转换为int,您可以将其传递给此函数,但例如在我们的例子中unique_ptr从未打算用作int, 所以很多开发人员从不直接使用operator bool,而是编写一个可以在条件表达式中用作布尔值的运算符,但它实际上不是布尔值!

struct bool_convertible {
typedef void (*convertible_to_bool)();
static void convertible_to_true();
operator convertible_to_bool () const {
return test() ? &convertible_to_true : nullptr;
}
};

使用这种技术,我可能有bool_convertible c; if( c ) {...}但我不能

void test_int( int );
bool_convertible c;
test_int( c );

在最近的 C++11 标准中,std::unique_ptr 没有定义operator!,只有显式转换运算符

explicit operator bool() const;

但是,内置的一元逻辑否定运算符!根据 5.3.1/9 在上下文中将其参数转换为布尔值:

逻辑否定运算符!的操作数在上下文中转换为bool(第 4 条);如果转换后的操作数为假,则其值为 true,否则为 false。

转换为 bool 的上下文将使用显式转换运算符(如果可用)。因此,您的代码实际上应该在 C++11 规则下工作。您可能应该向Microsoft提交错误报告。它们是否支持显式转换运算符并不重要。

作为解决方法,请尝试以下操作:

if (!fMinute.get() || !fIndividual.get()) {
....

最新更新