【问题标题】:SFINAE doesn't work on a constexpr function?SFINAE 不适用于 constexpr 函数?
【发布时间】:2015-10-14 22:37:07
【问题描述】:

为了支持可移植性,我想根据 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&lt;false, void*&gt;::type' has not been declared. template&lt;enable_if&lt;is_same&lt;size_t, uint64_t&gt;::value,void*&gt;::type = nullptr&gt;,此代码无法编译

编译器 - GCC 4.9

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

【问题讨论】:

  • 还有可能使用标签调度,这对于您的使用示例来说会更加优雅。 defaultSizeHelper(std::integral_constant&lt;std::size_t, sizeof(std::size_t)&gt;{})
  • 只是对我之前评论的一个小补充,您的代码在使用标签调度时将如下所示:coliru.stacked-crooked.com/a/ee0738c2369e03ec

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


【解决方案1】:

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

【讨论】:

  • 哦,我忘了我可以把元编程和普通代码混为一谈!感谢您提供一个很好的解决方案!
【解决方案2】:

问题

SFINAE代表替换失败不是错误

您的两个模板在实例化期间都不会失败,而是其中一个会在编译器查看它的第二秒失败(因为它会看到 enable_ifs 不依赖于模板参数,并尝试直接展开它们)。


解决方案

解决方案是让检查依赖于一些模板参数,这样编译器就只能在潜在的实例化时检查条件。

在您的情况下,最简单的解决方案是简单地提供一个默认模板参数,这是您要检查的类型(下面的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

【讨论】:

  • 这个解决方案更好(一般来说,不是在这种特殊情况下),因为它不会引入额外的虚拟(好吧,根本不是)参数。
猜你喜欢
  • 1970-01-01
  • 2012-10-29
  • 2020-12-08
  • 2012-02-23
  • 1970-01-01
  • 2012-06-16
  • 1970-01-01
  • 2019-12-05
  • 2013-02-20
相关资源
最近更新 更多