SFINAE没有处理constexpr功能?

时间:2015-10-14 22:37:08

标签: c++ templates c++11 constexpr enable-if

为了支持可移植性,我想根据size_t是32位还是64位这一事实选择一个常量。 代码:

using namespace std;

namespace detail {
    template<enable_if<is_same<size_t, uint32_t>::value,void*>::type = nullptr>
    constexpr static const size_t defaultSizeHelper() {
        return ( (size_t) 1 << 30 ) / 2 * 5; //2,5 Gb
    }
    template<enable_if<is_same<size_t, uint64_t>::value,void*>::type = nullptr>
    constexpr size_t defaultSizeHelper() {
        return numeric_limits<size_t>::max() / 2;
    }
}

constexpr static size_t defaultSize = detail::defaultSizeHelper();

由于error: 'std::enable_if<false, void*>::type' has not been declared. template<enable_if<is_same<size_t, uint64_t>::value,void*>::type = nullptr>

,此代码无法编译

编译器 - GCC 4.9

在我看来,编译器并没有将SFINAE原则应用于constexpr。那我该怎么办?

2 个答案:

答案 0 :(得分:5)

SFINAE背后的原理是,如果替换推导出的模板参数会导致格式错误的代码,那么该函数模板将从重载分辨率集中删除,而不是导致硬错误。

在您的情况下,没有推断的模板参数或替换一个,因此您最终会出现编译错误。你所需要的只是

constexpr static size_t defaultSize = is_same<size_t, uint32_t>::value 
                                        ? (( (size_t) 1 << 30 ) / 2 * 5)
                                        : numeric_limits<size_t>::max() / 2;

出于好奇,如果你想使用SFINAE,你可以做这样的事情

namespace detail {
    template<typename T, typename enable_if<is_same<T, uint32_t>::value,void*>::type = nullptr>
    constexpr static const T defaultSizeHelper(T) {
        return ( (size_t) 1 << 30 ) / 2 * 5; //2,5 Gb
    }
    template<typename T, typename enable_if<is_same<T, uint64_t>::value,void*>::type = nullptr>
    constexpr T defaultSizeHelper(T) {
        return numeric_limits<size_t>::max() / 2;
    }
}

constexpr static size_t defaultSize = detail::defaultSizeHelper(size_t{});

答案 1 :(得分:3)

问题

SFINAE 代表替换失败不是错误

在实例化过程中,两个模板都没有失败,而其中一个模板在第二个编译器看到它时会失败(因为它会看到 enable_if 不依赖于模板参数,并尝试直接扩展它们。)

解决方案

解决方案是使检查取决于某些 template-parameter ,以便编译器只能在潜在的实例化时检查条件。

在您的情况下,最简单的解决方案是简单地提供默认模板参数,这是您要检查的类型(下面的T)。

using namespace std;

namespace detail {
    template<class T = uint32_t, typename enable_if<is_same<size_t, T>::value,void*>::type = nullptr>
    constexpr static const size_t defaultSizeHelper() {
        return ( (size_t) 1 << 30 ) / 2 * 5; //2,5 Gb
    }

    template<class T = uint64_t, typename enable_if<is_same<size_t, T>::value,void*>::type = nullptr>
    constexpr size_t defaultSizeHelper() {
        return numeric_limits<size_t>::max() / 2;
    }
}

constexpr static size_t defaultSize = detail::defaultSizeHelper();
  

注意:另一种解决方案是将两个函数合并为一个,并使用ternary-operator返回一个表达式或另一个表达式的结果。

  

注意:既然检查依赖于模板参数,请确保您了解为什么需要使用typename来消除启用if的歧义。有关详细信息,请参阅this answer