SFINAE not working on constexpr function?

To maintain portability, I want to choose a constant based on whether size_t 32-bit or 64-bit. Code:

 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(); 

This code does not compile due to error: 'std::enable_if<false, void*>::type' has not been declared. template<enable_if<is_same<size_t, uint64_t>::value,void*>::type = nullptr> error: 'std::enable_if<false, void*>::type' has not been declared. template<enable_if<is_same<size_t, uint64_t>::value,void*>::type = nullptr>

Compiler - GCC 4.9

It seems to me that the compiler does not apply the SFINAE principle to constexpr . What should I do then?

+5
source share
2 answers

Problem

SFINAE means replacement failure is not an error.

None of your two templates will work during instance creation, instead, one of them will not be able to execute the second one, which the compiler will look through (because it sees that enable_ifs parameters are independent of the template parameter and try to deploy them directly).


Decision

The solution is to make the check dependent on some template parameter, so that the compiler can only check the condition for the potential instance.

In your case, the easiest solution would be to simply provide a default argument template, which is the type you would like to test ( T in below).

 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 two functions into one and use the thermal operator for any that returns the result of one expression or the other. .

Note Now that the check depends on the template parameter, make sure that you understand why you need to use typename to eliminate the possibility of including if. See this answer for more information.

+3
source

The principle of SFINAE is that if the substitution of the calculated template argument leads to poorly formed code, then this function template is reset from the set overload resolution, instead of causing a hard error.

In your case, there is no template argument output or replacement, so you get compilation errors. All you need is

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

For curiosity, if you want to use SFINAE, you can do something like this

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

Source: https://habr.com/ru/post/1233743/


All Articles