4

我遇到了一些用 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 functions 模棱两可,因为它不知道该使用哪一个。从我读过的内容来看,重载决议最不喜欢省略号参数(...)。因此,它应该知道选择第一个。

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

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

4

2 回答 2

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;
};

如果您确实需要将其用于 SFINAE(即基于模板参数禁用/启用功能),只需将上述内容与Boostsenable_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,你应该能够从这里使用解决方案:

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更具描述性的东西:)

于 2011-02-02T09:17:48.470 回答
6

在我的评论中,我说过您通常不会将这些谓词的结果用作 an 的值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>());
}

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

于 2011-02-02T09:36:20.810 回答