【问题标题】:Overloaded functions are ambiguous when using SFINAE principle使用 SFINAE 原理时重载函数不明确
【发布时间】:2011-02-02 09:04:22
【问题描述】:

我遇到了一些用 VS7.1 编写的代码,现在我正试图让它在 MacOSX 上工作。下面我理解的代码sn-p是关于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,就像这里建议的那样。问题是特定于模板参数的推导方式,并且它没有按照设置的方式工作。但是,按照 Georg 在回答末尾的建议,我可以纠正一些事情以接受事情。我现在有以下内容:

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 函数中有两个函数参数会导致问题。我还没找到原因。当我更好地理解事情时,我会回到这个。

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

【问题讨论】:

  • 你能稍微扩展一下你的代码吗?此外,您通常不希望将元谓词的结果作为 if 分支的值,因为错误分支中的代码将尝试使用不起作用的代码,您可以通过选择两个来拆分内容不同的功能。 IsChar 是不是命名错误?它似乎并没有检查Tchar
  • @aaa:我在模板方面的技能水平已经超出了我的水平,所以您能否详细说明您对 enable_if 的引用的含义?谢谢。
  • @GMan:您能否详细说明在 if 中不使用它的意思?我在这里学习,所以我们需要慢慢来:)
  • 我添加了一个补充答案。

标签: c++ templates sfinae


【解决方案1】:

除非我误解了意图,否则上述使用示例不需要使用 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() {
}

或者,如果你可以一路 Boost:

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 之类的描述性更强:)

【讨论】:

  • 感谢您的回答。等我回家后,我将不得不检查原始代码。还有另一个层使用类似于我发布的代码的东西,所以也许它包含一些在这里很重要的更多信息。我会回到这个。
  • @murrekatt:当然,有关您需要的上下文的更多信息,让我们提供更好的答案。
  • 感谢其他问题的链接。非常好的信息,这正是我所看到的。只是实现方式略有不同。
【解决方案2】:

在我的评论中,我说过您通常不会将这些谓词的结果用作 if 的值,这就是为什么:

// assume we have has_typedef_type from the Wikipedia page:

template <typename T>
void foo(const T& x)
{
    if (has_typedef_type<T>::value)
    {
        // the predicate is true, so T::type exists
        typename T::type y = x;
    }
    else
    {
        // the predicate is false, so T::type doesn't exist, do something else
        float y = x;
    }
}

这看起来不错,但考虑到当谓词为假时,编译器会尝试编译它:

// let's say we called it with double
void foo<double>(const double& x)
{
    if (false)
    {
        // wait, double doesn't have this!
        typename double::type y = x;
    }
    else
    {
        float y = x;
    }
}

问题在于代码,即使它会通过删除死代码而被删除,但它的格式不正确。解决方案是使if 编译时也是如此,但首先是一些样板:

// in C++0x, these are defined in <type_traits>, but we'll do it ourselves
// (Boost has these as well)
typename <typename T, T Value>
struct integral_constant
{
    typedef T type;
    static const T value = Value;
};

typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;

除此之外,我们定义我们的函数:

namespace detail
{
    // here are the real implementations
    template <typename T>
    void foo(const T& x, true_type)
    {
        // the predicate is true, so T::type exists
        typename T::type y = x;
    }

    template <typename T>
    void foo(const T& x, false_type)
    {
        // the predicate is false, so T::type doesn't exist, do something else
        float y = x;
    }
}

template <typename T>
void foo(const T& x)
{
    detail::foo(x, // chose which function to call, using the type of this:
                integral_constant<bool, has_typedef_type<T>::value>());
}

现在一切正常,因为分支完全相互独立。

【讨论】:

  • 非常感谢那个 GMan。我明白。而且我还记得原始代码使用 boost::integral_constant 作为下一层。基本上,就像你展示的那样。只是在我的示例中,我错误地使用了它来快速给出显示错误的内容。无论如何,经过您的解释,我现在对事情的理解好多了。今晚我会用更多信息更新这个问题。
  • @murre:很高兴这是有道理的。 :)
  • 很高兴看到您仍然提供高质量的帮助 GMan :)
  • @Georg:谢谢,还是试试吧。 :)
猜你喜欢
  • 1970-01-01
  • 2012-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多