2

I'm toying with SFINAE and I try to check my Inputs is made of Input of various types. The error provided by clang doesn't help much. Do you have any idea ?

Thanks

struct IsFree
{
};

template <typename _Type, typename _State>
struct Input
{
};

template <typename... _Inputs>
struct Inputs
{
};

template <template <typename _Type, typename _State> class, typename... _Inputs>
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
{
};

Somewhere else :

auto temp = Inputs<Input<float, IsFree>, Input<float, IsFree>> {};

I get using clang-5.0 and -std=c++17 :

13 : <source>:13:21: error: use of undeclared identifier '_Type'
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
                    ^
13 : <source>:13:35: error: expected a type
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
                                  ^
2 errors generated.
Compiler exited with result code 1
dalle
  • 18,057
  • 5
  • 57
  • 81
sylvain
  • 35
  • 5
  • 1
    Beware, identifiers starting with an underscore followed by an upper case letter are reserved. This is undefined behavior to declare one. Rule under [`[lex.name]/3.1`](http://eel.is/c++draft/lex.name#3.1). – YSC Nov 22 '17 at 12:14
  • 4
    **SFINAE**, not SNIFAE. Substitution Failure Is Not An Error. – Constructor Nov 22 '17 at 12:22

3 Answers3

6
template <template <typename _Type, typename _State> class, typename... _Inputs>
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
{
};

needs to be

template <typename _Type, typename _State, typename... _Inputs>
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
{
};

in the pattern Input<_Type, _State> _Type and _State are just type wildcards, you only need the template <typename, typename> class F template template parameter syntax if you need to match a template template parameter with a wild card. In this case you are matching the template with a known template named Input

odinthenerd
  • 5,422
  • 1
  • 32
  • 61
5

Your partial specialization for the last case is not correct. You need to deduce _Type and _State, not have a template template parameter.

template <class _Type, class _State, typename... _Inputs>
struct Inputs<Input<_Type, _State>, _Inputs...> : public Inputs<_Inputs...>
{
};

In your original code, the names inside the template template parameter do not introduce template parameters for that partial specialization.

Also note that names beginning with an underscore and capital letter are reserved to the implementation, so you should not use them in your own code.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • You mention the underscore I use. What are you calling the implementation ? – sylvain Nov 22 '17 at 12:12
  • @sylvain The compiler and standard library. – TartanLlama Nov 22 '17 at 12:16
  • Is there any convention ? The _ is quite convenient. – sylvain Nov 22 '17 at 12:47
  • 1
    @sylvain The most common way is to just capitalize template parameters and not use the underscore. – oisyn Nov 22 '17 at 13:08
  • @sylvain avoiding `_Names _Like _This` is not just a convention, it's a rule that you must follow or be destroyed^W gently corrected. See [reserved names](http://stackoverflow.com/q/228783/981959). It's not "convenient" to litter your code with unnecessary noise, and it's certainly not convenient to risk making your code fail to compile because you used names reserved for the implementation. – Jonathan Wakely Nov 22 '17 at 13:27
0

Other than the perfect answers already provided by others, may I ask what your expected outcome is of a type like Inputs<Input<float, IsFree>, int, Input<int, IsFree>>? Notice the stray int in the middle. Would you like the base class recursion to stop at the first non-Input<> argument, as currently is the case in your example, or rather have a compile error?

If the latter, might I suggest changing the general Inputs template definition to be an incomplete type, and then specializing for an empty struct in case of zero template arguments, like so:

// General case, incomplete type
template<class... T>
struct Inputs;

// Special case for zero arguments, empty struct
template<>
struct Inputs<>
{
};

Together with your specialization for Inputs<Input<*,*>, ...>, this will make sure you can never instantiate a template using arguments that are not a list of Input<*,*>.

oisyn
  • 1,306
  • 5
  • 14
  • I provided a small sample of the whole thing for solving my compilation failure. But you are true there is a lot of termination cases to handle in my code. – sylvain Nov 22 '17 at 13:38