首页 > 解决方案 > 强制转换为 void 以避免使用重载的用户定义逗号运算符

问题描述

我正在学习 C++ 中的模板,并遇到了一个void使用强制转换的示例:

template<typename T>
auto func (T const& t) -> decltype( (void)(t.size()), T::size_type() )
{
return t.size();

}

在解释中写道:

将表达式强制转换为 void 是为了避免用户定义的逗号运算符为表达式类型重载的可能性。

我的问题是/是:

  1. 如何使用强制转换为 void 来“避免用户定义的逗号运算符为表达式类型重载的可能性”?我的意思是,谁能举void个例子,如果我们不使用,那么这段代码会出错?例如,假设我们有一个名为的类SomeClass,它重载了逗号运算符。现在,如果我们不使用,这会成为问题void吗?

  2. 可以static_cast在这种情况下使用而不是 C 样式转换吗?例如,类似static_cast<void>(t.size()). 我正在阅读使用 C++17 特性的示例,所以我想知道为什么作者在这种情况下使用了 C 样式转换。

我已经阅读了强制转换为 `void` 的真正作用是什么?,从中我得到的印象是,如果我们使用(void)xthen 这意味着禁止编译器警告,也意味着“忽略 x 的值”。但是后来我无法理解表达式x和之间的区别(void)x

标签: c++c++11castingc++17void

解决方案


考虑这种病理类型:

struct foo {
    struct size_type {
        bool operator,(size_type) { return false;}
    };
    size_type size() { return {};}  
};

它确实有一个size_type并且确实有一个size()方法。但是,如果没有转换为void,则模板不会推断出正确的返回类型,因为decltype( (t.size()), typename T::size_type() )is bool

#include <type_traits>

template<typename T>
auto func (T const& t) -> decltype( (t.size()), typename T::size_type() )
{
return t.size();

}

struct foo {
    struct size_type {
        bool operator,(size_type) { return false;}
    };
    size_type size() const { return {};}  
};


int main()
{
   func(foo{});
}

结果错误

<source>:6:8: error: no viable conversion from returned value of type 'foo::size_type' to function return type 'decltype((t.size()) , typename foo::size_type())' (aka 'bool')
return t.size();
       ^~~~~~~~
<source>:20:4: note: in instantiation of function template specialization 'func<foo>' requested here
   func(foo{});
   ^
1 error generated.
ASM generation compiler returned: 1
<source>:6:8: error: no viable conversion from returned value of type 'foo::size_type' to function return type 'decltype((t.size()) , typename foo::size_type())' (aka 'bool')
return t.size();
       ^~~~~~~~
<source>:20:4: note: in instantiation of function template specialization 'func<foo>' requested here
   func(foo{});
   ^

static_cast可以使用一个。但是,由于实际上没有进行任何转换(这是一个未评估的上下文),因此 c 样式转换不会造成太大的伤害。请注意如何绕过void对用户定义operator,的强制转换并推断出正确的返回类型:https ://godbolt.org/z/jozx1YGWr 。这是因为void, whatever使用了operator,结果与whatever. 您不能void, whatever使用用户定义的operator,; 没有有效的语法。

我想代码只是为了说明这一效果,因为即使强制转换为voidone 也可以弥补其他失败的示例(例如,模板没有明确地测试t.size()实际返回T::size_type)。


另请参阅此处的“很少重载运算符”部分:

逗号运算符,operator,. 与内置版本不同,重载不会将其左操作数排序在右操作数之前。(直到 C++17)因为此运算符可能会被重载,所以泛型库使用诸如a,void(),b代替a,b序列执行用户定义类型的表达式的表达式。boost 库在 boost.assign、boost.spirit 和其他库中使用运算符。数据库访问库 SOCI 也重载operator,


推荐阅读