C++的类型转换重载(type casting overriding)

最近项目里面用了mysqlpp,注意到query对象在获取查询结果时候的store()方法返回了StoreQueryResult,虽然它不是指针但是可以这样用:

if (StoreQueryResult queryResult = query.store()) {...

一开始以为写错了——而且就算是运算符重载,不也只能用“!”么。查了一下相关的文档,原来这玩意是类型转换重载。

operator关键字后面的类型任意,甚至可以是void*,显而易见在这种情况下需要这么做:

private:
    bool _isValid;
public:
    operator void*() const { return _isValid ? this : nullptr; }

这样一来也可以达到上面type casting的效果。

但这几种方式或多或少都存在问题,所以The Safe Bool Idiom介绍了另外一种方式:嵌套类/安全类型转换。可以看到mysqlpp用的也是这种方式。文章最后还利用模板创建了支持多类型的版本。

这个故事告诉我们,C++的细节太多了。

p.s:想到了C#的nullable type。

在C# 2.0之前,类似int这种值类型是不能和null比较的,所以总需要提供一个getter或者其他什么method进行bool判断。有了nullable type后,就可以用int? x = 0; 这样的语法创建可和null比较的值类型对象。

那个问号其实只是语法糖,真正实现功能的是System.Nullable。嗯,到了这里就和之前看到的reusable version of type casting很像了吧。用ILSpy或者其他什么工具打开mscorlib.dll,可以看到Nullable在实现上是这样(省略一些细节):

namespace System
{
	[TypeDependency("System.Collections.Generic.NullableComparer`1"), TypeDependency("System.Collections.Generic.NullableEqualityComparer`1")]
	[Serializable]
	public struct Nullable<T> where T : struct
	{
		private bool hasValue;
		internal T value;
		public T Value
		{
			[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
			get
			{
				if (!this.HasValue)
				{
					ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoValue);
				}
				return this.value;
			}
		}
		[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
		public Nullable(T value)
		{
			this.value = value;
			this.hasValue = true;
		}
		[TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
		public static implicit operator T?(T value)
		{
			return new T?(value);
		}
		public static explicit operator T(T? value)
		{
			return value.Value;
		}
	}
}

说白了还是privaate flag + getter + conversion method。最后两个方法,就是C#版本的type casting overriding。

Leave a Reply