How to use std :: enable_if with a condition that itself depends on another condition?

I have a situation where I need to distinguish between two overloads, say foo , using std::enable_if . The condition specified by std::enable_if depends on the dependent type of the template parameter foo .

What is the best way to express this with std::enable_if ?

The following test code is what I have. I understand that there are probably better ways than std::enable_if to achieve the behavior that I want in the test code. However, the next simplified version of my use case, which itself requires std::enable_if .

 #include <type_traits> #include <cassert> struct bar { using baz = int; }; template<class T> struct is_bar : std::false_type {}; template<> struct is_bar<bar> : std::true_type {}; template<class Bar> struct baz_type { using type = typename Bar::baz; }; template<class T> typename std::enable_if< std::is_integral< typename baz_type<T>::type >::value, int >::type foo(T x) { return 7; } template<class T> typename std::enable_if< !is_bar<T>::value, int >::type foo(T x) { return 13; } int main() { assert(foo(bar()) == 7); assert(foo(0) == 13); return 0; } 

Compiler Output:

 $ g++ --version ; echo ; g++ -std=c++11 repro.cpp g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2 Copyright (C) 2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. repro.cpp: In instantiation of 'struct baz_type<int>': repro.cpp:29:3: required by substitution of 'template<class T> typename std::enable_if<std::is_integral<typename baz_type<Bar>::type>::value, int>::type foo(T) [with T = int]' repro.cpp:49:3: required from here repro.cpp:18:33: error: 'int' is not a class, struct, or union type using type = typename Bar::baz; 

This code does not compile, because enable_if , used during the first foo overload, depends on the nested type T::baz . Since int does not have this nested type, the code is illegal.

What is the right way to express what I want?

+5
source share
1 answer

While I wrote below about Coliru, @dyp already showed the important part in his comment. The following is what will work, and what is, IMHO, is quite readable:

 template< class T, typename=typename std::enable_if<is_bar<T>::value>::type, typename=typename std::enable_if<std::is_integral<typename baz_type<T>::type>::value>::type > int foo(T x) { return 7; } template< class T, typename=typename std::enable_if<!is_bar<T>::value>::type > int foo(T x) { return 13; } 

Living example

With C ++ 14, you can use std::enable_if_t to make it even shorter.

+2
source

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


All Articles