使用SFINAE原理时,重载功能不明确

mur*_*att 4 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);
};
Run Code Online (Sandbox Code Playgroud)

我只是这样使用这个:

if (IsChar<int>::Value)
{
    ...
Run Code Online (Sandbox Code Playgroud)

编译上面的代码时,由于在使用int时缺少Type的typedef,它会选择最顶层的类.

如果我现在使用char而不是......

if (IsChar<char>::Value)
{
    ...
Run Code Online (Sandbox Code Playgroud)

... 编译器会抱怨模糊的Select函数,因为它不知道使用哪一个.从我读过的重载决议中,最不喜欢省略号参数(...).因此,它应该知道选择第一个.

代码在至少VS7.1上运行良好,但不是用于MacOSX的gcc而不是用于Linux的gcc4.4.

有任何建议如何纠正这个?也许它通常以另一种方式完成?

谢谢!

更新:我意识到我给出的示例代码可能稍微简化了一些,因为我相信我们不会在这里检查类型,即使我错误地让它看起来像那样.今晚我将不得不为你收集更多信息,因为我这里没有代码.对不起.

更新2:即使我的陈述不好,也是因为不熟悉原始代码或以这种方式使用模板.同时我挖出了更多的信息,让我们假设这些结构是由于某种原因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);
Run Code Online (Sandbox Code Playgroud)

这很好用.在尝试一下时,我发现在Select函数中有两个函数参数会导致问题.我没有找到原因.当我更好地理解事情时,我会回到这里.

感谢你的帮助.至少我现在理解这里的原则以及事情应该如何运作.只有一些细节,仍然是未知数.

Geo*_*che 9

除非我误解了意图,否则上述使用样本不需要使用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;
};
Run Code Online (Sandbox Code Playgroud)

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

template<class T> 
typename boost::enable_if_c<IsChar<T>::Value, void>::type
someFunction() {
}
Run Code Online (Sandbox Code Playgroud)

或者如果你可以一路狂奔:

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

更新:

重读这个,如果你真的想检查一下SomeClass有一个typedef的特化Type,你应该能够使用来自这里的解决方案:

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);
};
Run Code Online (Sandbox Code Playgroud)

在那种情况下IsChar肯定是用词不当,有点像HasType或者HasTypedefType更具描述性:)


GMa*_*ckG 6

在我的评论中,我说你通常不会将这些谓词的结果用作a的值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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这可能看起来很好,但考虑谓词是假的时候,编译器会尝试编译它:

// 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是代码,即使它将被删除死代码删除,也是不正确的.解决方案是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;
Run Code Online (Sandbox Code Playgroud)

有了这个,我们定义我们的功能:

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>());
}
Run Code Online (Sandbox Code Playgroud)

现在一切都很好,因为分支完全独立.