SFINAE detect if type is defined
Asked Answered
S

1

6

I want to pick a specialization of a template when a certain type is defined.

I still cannot wrap my head around SFINAE :(. I might be close or I might be completely off. I tried different things and this is something, I at least hope to understand why it does not work (is_complete basically stolen from here):

#include <iostream>
#include <type_traits>

template <typename T, class = void>
struct is_complete : std::false_type {};

template <typename T> 
struct is_complete<T,decltype(void(sizeof(T)))> : std::true_type {};

// this should be called if foo is not defined
void test() { std::cout << "test base\n"; }

// forward declare foo
struct foo;

// this should be called if foo is defined    
template <typename T>
std::enable_if<is_complete<foo>::value,void> test() {
  foo::bar();
}

// this is either defined or not
struct foo{
  static void bar() { std::cout << "foo bar\n"; }
};

int main(){
  test();
}

With gcc 4.8 (-std=c++11) i get :

if_type_defined.cpp: In instantiation of ‘struct is_complete<foo>’:
if_type_defined.cpp:16:32:   required from here
if_type_defined.cpp:8:42: error: invalid application of ‘sizeof’ to incomplete type ‘foo’
 struct is_complete<T,decltype(void(sizeof(T)))> : std::true_type {};
                                          ^
if_type_defined.cpp:8:42: error: invalid application of ‘sizeof’ to incomplete type ‘foo’
if_type_defined.cpp: In function ‘std::enable_if<true, void> test()’:
if_type_defined.cpp:17:3: error: incomplete type ‘foo’ used in nested name specifier
   foo::bar();
   ^

I think I know more or less what is wrong: foo does not depend on T, hence no substitution needed to get foo and I get a hard error instead of Not An Error. Next I tried to use a helper along the line of

template <typename T>
struct make_foo_dependent { 
   using type = foo;
};

and tried to use that inside the enable_if instead of foo directly. However, this just added more errors and I didnt include it here, because I am afraid this is also going in the wrong direction.

How can I choose what function to call depending on whether foo is defined? If foo is not defined, the code using foo should not issue a hard error, but simply be ignored by the compiler.

PS: Lots has changed with respect to SFINAE, and I find it hard to find ressources that restrict themself to C++11, where things seem to be a bit more hairy than in newer standards.

Susie answered 23/8, 2019 at 10:28 Comment(5)
"this should be called if foo is defined" - but test(); will never invoke that function because it is a template that requires at least one argument.Neve
@VTT oh, right :). As I mentioned, I am on a complete loss on SFINAE, I just try random stuff and rarely I get what i wantSusie
The trick with make_foo_dependent should've worked. It is actually a right direction.Neve
@VTT it compiles , but it always calls the non-template foo, I guess it can only work when also the base case is a template. Dont remember where i got the idea that it can be non-templateSusie
Care with struct is_complete and code (SFINAE) based on it. You quickly can have ODR violations NDR.Harquebus
A
6

Yes, as you said, you should make test depending on the template parameter T; and better making both the overloads template. e.g.

// this should be called if foo is not defined
template <typename T = foo>
typename std::enable_if<!is_complete<T>::value,void>::type test() { std::cout << "test base\n"; }

// this should be called if foo is defined    
template <typename T = foo>
typename std::enable_if<is_complete<T>::value,void>::type test() {
  T::bar();
}

then call it as

test(); // or test<foo>();

LIVE (foo is defined)
LIVE (foo is not defined)

BTW: From your intent, I think the return type of test should be typename std::enable_if<is_complete<T>::value,void>::type instead of std::enable_if<is_complete<foo>::value,void>; which is just the type of an instantiation of std::enable_if itself.

Amyamyas answered 23/8, 2019 at 10:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.