I have code like this:
#include <type_traits>
struct S1{};
struct S2{
void rbegin(){}
void rend(){}
};
template<typename S>
struct S3{
void rbegin(){
S().rbegin();
}
void rend(){
S().rend();
}
};
template<typename, typename = void>
constexpr bool is_reverse_iterable = false;
template<typename T>
constexpr bool is_reverse_iterable<
T,
std::void_t<
decltype(std::declval<T>().rbegin()),
decltype(std::declval<T>().rend())
>
> = true;
#include <iostream>
int main(){
std::cout << is_reverse_iterable<S1> << '\n'; // print 0, correct
std::cout << is_reverse_iterable<S2> << '\n'; // print 1, correct
std::cout << is_reverse_iterable<S3<S1> > << '\n'; // print 1, wrong !!!
std::cout << is_reverse_iterable<S3<S2> > << '\n'; // print 1, correct
// S3<S1>().rbegin(); // not compiles
S3<S2>().rbegin();
}
Example is copy / paste from cppreference.com website, however, is_reverse_iterable<S3<S1> > is set to true and this is wrong.
How can I fix this?
Godbolt link: https://gcc.godbolt.org/z/qjG46EWsq
The methods of
S3are not SFINAE-friendly. (During SFINAE, the compiler won't look inside the function body for substitution errors. It'll either ignore them if the function body isn't needed yet, or fail with a hard error on them if the body is needed, e.g. if the return type isauto.)In C++20 you would fix them like this:
In C++17 you need to do something like this:
Another C++17 option: