我的情况如下:
我有一个模板包装器,它可以处理值和对象可以为空的情况,而无需手动处理指针甚至new
. 这基本上归结为:
struct null_t
{
// just a dummy
};
static const null_t null;
template<class T> class nullable
{
public:
nullable()
: _t(new T())
{}
nullable(const nullable<T>& source)
: _t(source == null ? 0 : new T(*source._t))
{}
nullable(const null_t& null)
: _t(0)
{}
nullable(const T& t)
: _t(new T(t))
{}
~nullable()
{
delete _t;
}
/* comparison and assignment operators */
const T& operator*() const
{
assert(_t != 0);
return *_t;
}
operator T&()
{
assert(_t != 0);
return *_t;
}
operator const T&() const
{
assert(_t != 0);
return *_t;
}
private:
T* _t;
};
现在使用比较运算符,我可以检查null_t
虚拟对象,以便在实际尝试检索值或将其传递给需要该值并进行自动转换的函数之前查看它是否设置为 null。
这门课在很长一段时间内对我很有帮助,直到我偶然发现了一个问题。我有一个包含一些结构的数据类,这些结构将全部输出到一个文件(在本例中为 XML)。
所以我有这样的功能
xml_iterator Add(xml_iterator parent, const char* name,
const MyDataStruct1& value);
xml_iterator Add(xml_iterator parent, const char* name,
const MyDataStruct2& value);
每个都用适当的数据填充 XML-DOM。这也可以正常工作。
然而,现在这些结构中的一些是可选的,在代码中将被声明为
nullable<MyDataStruct3> SomeOptionalData;
为了处理这种情况,我做了一个模板重载:
template<class T>
xml_iterator Add(xml_iterator parent, const char* name,
const nullable<T>& value)
{
if (value != null) return Add(parent, name, *value);
else return parent;
}
在我的单元测试中,正如预期的那样,编译器总是更喜欢选择这个模板函数,无论值或结构包含在nullable<T>
.
但是,如果我使用上述数据类(在其自己的 DLL 中导出),由于某种原因,第一次应该调用最后一个模板函数,而是完成从nullable<T>
到相应类型的自动转换T
,完全绕过函数意味着来处理这个案子。正如我上面所说的——所有单元测试都 100% 正常,测试和调用代码的可执行文件都是由 MSVC 2005 在调试模式下构建的——这个问题绝对不能归因于编译器的差异。
更新:澄清 - 重载Add
函数不导出,仅在 DLL 内部使用。换句话说,遇到这个问题的外部程序甚至不包括带有模板重载函数的头部。