With CRTP idiom in C++11 I'm trying to check out if a derived class has a method named size or not.
#include <iostream>
template<typename Derive> struct IsContainer {
template<typename Der> static constexpr bool has_size( decltype(&Der::size) ) { return true; }
template<typename Der> static constexpr bool has_size(...) { return false; }
static constexpr bool value = has_size<Derive>(nullptr);
};
struct Container : IsContainer<Container> {
const unsigned int size();
};
struct NonContainer : IsContainer<NonContainer> {};
int main() {
Container obj;
std::cout << obj.value <<std::endl;
}
I then tried to get rid of template <typename Der> as following:
#include <iostream>
template<typename Derive> struct IsContainer {
static constexpr bool has_size( decltype(&Derive::size) ) { return true; }
static constexpr bool has_size(...) { return false; }
static constexpr bool value = has_size(nullptr);
};
struct Container : IsContainer<Container> {
const unsigned int size();
};
struct NonContainer : IsContainer<NonContainer> {};
int main() {
Container obj;
std::cout << obj.value <<std::endl;
}
Compilation error:
In instantiation of ‘struct IsContainer<Container>’:
error: incomplete type ‘Container’ used in nested name specifier
| static constexpr bool has_size( decltype(&Derive::size) ) { return true; }
| ^~~~~~~~
I encountered such a statement quoted from standard explaining why does compiler gives this error
3.3.2p6 Declaration point [basic.scope.pdecl]
Once a class member has been declared, its name can be searched for within the scope of the class
I still do not get that for which reason the first code snippet does work and the second not. I really would appreciate if someone explains it to me with detail and possibly give me a hint to get around the error of the second code snippet.
By removing the template-head
template <typename Der>, there can be no SFINAE for those member and so we get a hard error. SFINAE happens when during substitution of the template parameter, and without a template-head there is no substitution and hence no SFINAE. Note that in the second snippet,Deriveis a template parameter of the class template and not the member function itself.Containerisn't complete until the closing brace. Sodecltype(&Derive::size)in the member function parameter will give a hard error without the template head as no sfinae can happen unlike the first case. See the contrived example given at the end of the answer.When you wrote
IsContainer<Container>thenDerive= Containerand sinceContainerisn't complete at this point we can't writedecltype(&Derive::size)which basically isdecltype(&Container::size)in the parameter declaration. Note also that declarations of member functions are instantiated with the class template. And since in the second snippet, you don't have a template-head i.e.,Deriveis a template parameter of the class not the member function, we don't get SFINAE and instead get a hard error.Contrived Example
Below is a convtrived example to illustrate the same. In particular, for the following program we will get a hard error as no SFINAE happens for
func.The error you get for the above program says:
Now, let's look at another example where SFINAE happens and we don't get a hard error: