SFINAE 不适用于 constexpr 函数?
SFINAE doesn't work on a constexpr function?
为了支持可移植性,我想根据 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
。那我该怎么办?
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{});
问题
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();
Note: An alternative solution would be to combine the two functions into one, and use the ternary-operator to either return the result of one expression, or another..
Note: Now that the check is dependent on a template-parameter, make sure you understand why you need to use typename
to disambiguate the enable if. See this answer for more information.
为了支持可移植性,我想根据 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
。那我该怎么办?
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{});
问题
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();
Note: An alternative solution would be to combine the two functions into one, and use the ternary-operator to either return the result of one expression, or another..
Note: Now that the check is dependent on a template-parameter, make sure you understand why you need to use
typename
to disambiguate the enable if. See this answer for more information.