c++ - 使用 SFINAE 原理时重载函数有歧义

标签 c++ templates sfinae

我遇到了一些用 VS7.1 编写的代码,现在我正试图让它在 MacOSX 上工作。我理解的下面的代码片段关于SFINAE原则。据我了解,代码用于在编译时通过依赖一些模板实例化魔法来知道什么类型。简而言之,通过查看模板参数来选择正确的重载。

这是我的代码。稍微简化以仅显示问题。

template <typename T>
struct SomeClass
{
};

template <>
struct SomeClass<char>
{
    typedef char Type;
};

template <typename T>
struct IsChar
{
    typedef char Yes;
    typedef int No;

    template <typename U>
    static Yes Select(U*, typename SomeClass<U>::Type* p = 0);
    template <typename U>
    static No Select(U*, ...);
    static T* MakeT();

    const static bool Value = sizeof(Select(MakeT())) == sizeof(Yes);
};

我只是像这样使用它:

if (IsChar<int>::Value)
{
    ...

编译时上面的代码运行良好,并且由于使用 int 时缺少 Type 的 typedef,它会选择最顶层的类。

如果我现在改用 char...

if (IsChar<char>::Value)
{
    ...

...编译器会提示不明确的 Select 函数,因为它不知道要使用哪一个。从我读过的内容来看,重载决议对省略号参数 (...) 的优先级最低。因此,它应该知道选择第一个。

代码至少在 VS7.1 上运行良好,但在 MacOSX 的 gcc 和 Linux 的 gcc4.4 上不行。

有什么建议可以纠正这个问题吗?也许它通常以另一种方式完成?

谢谢!

更新:我意识到我提供的示例代码可能稍微过于简化了,因为我相信我们并没有在这里检查类型,即使我错误地让它看起来像那样。今晚我必须为您收集更多信息,因为我这里没有代码。对此感到抱歉。

UPDATE2:即使我的演讲很糟糕,这是因为不熟悉原始代码或以这种方式使用模板。与此同时,我挖掘了更多信息,假设这些结构出于某种原因存在 X 并且我给出的名称都是错误的,那么编译器问题呢?为什么不能在这里选择正确的重载函数?这也是我感兴趣的。正如我所说,我会更好地解释总体目标是什么。

编辑

仔细查看原始代码后,它使用了 boost::integral_constant 和 boost::enable_if,就像这里建议的那样。问题是特定于如何推导模板参数并且它没有按照设置的方式工作。但是,按照乔治在他的回答末尾提出的建议,我可以纠正事情以接受事情。我现在有以下内容:

typedef char Yes;
typedef int No;

template <typename U> static Yes Select(typename SomeClass<U>::Type* p);
template <typename U> static No Select(...);

static const bool Value = sizeof(Select<T>(0)) == sizeof(Yes);

这很好用。在进行一些试验时,我发现在 Select 函数中有两个函数参数会导致问题。我还没找到原因。当我更好地理解事情时,我会回到这个问题上。

感谢您的帮助。至少我现在明白这里的原则以及事情应该如何运作。只有一些细节,目前还不得而知。

最佳答案

除非我误解了意图,否则上述用法示例不需要使用 SFINAE。如果你只想静态断言 Type 的类型,你可以使用这样的东西:

template<class T1, class T2> struct SameType {
    static const bool Value = false;
};

template<class T> struct SameType<T, T> {
    static const bool Value = true;
};

template <typename T>
struct IsChar {
    static const bool Value = SameType<T, char>::Value;
};

如果你真的需要将它用于 SFINAE(即基于模板参数禁用/启用功能),只需将上面的内容与类似 Boosts enable_if 的内容结合使用:

template<class T> 
typename boost::enable_if_c<IsChar<T>::Value, void>::type
someFunction() {
}

或者如果你可以一路提升:

template<class T> 
typename boost::enable_if<boost::mpl::is_same<T, char>, void>::type
someFunction() {
}

更新:

重读这篇文章,如果你真的想检查 SomeClass 的特化是否有 typedef Type,你应该能够使用来自 over here 的解决方案:

template<class T> struct HasType {
    template<class U> static char (&test(typename U::Type const*))[1];
    template<class U> static char (&test(...))[2];
    static const bool Value = (sizeof(test< SomeClass<T> >(0)) == 1);
};

在那种情况下,IsChar 肯定是用词不当,HasTypeHasTypedefType 之类的名称会更具描述性:)

关于c++ - 使用 SFINAE 原理时重载函数有歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4872474/

相关文章:

c++ - 模板外部链接?谁能解释一下?

c++ - 为什么我在插入 map 时会出现段错误

c++ - Rcpp 代码使 R 崩溃

C++ 是否可以构建模板类型列表?

c++ - 使用具有尾随返回类型的 lambda 尝试 SFINAE 时出现硬错误

c++ - 对于什么样的 'systems programming' 项目,C++ 明显优于 C?

c++ - 要求派生类提供成员函数模板

c++ - union 可以模板化吗?

c++ - SFINAE 启用/禁用功能和模板别名

c++ - 与 SFINAE 中的硬错误混淆