Home > OS >  compile time parameter checking
compile time parameter checking

Time:07-05

I'm trying to understand how the Qt5 framework's Signals and Slots mechanism does type checking on function arguments.

I've provided a summary of the section of code I'm interested in. The only thing I'm having trouble understanding is the following template specialization. How does the compiler decide to instantiate this? I especially don't understand when template parameter list template< typename Arg1, typename Arg2, typename... Tail1, typename... Tail2 > is considered.

template< typename Arg1, typename Arg2, typename... Tail1, typename... Tail2 >
  struct Test< List< Arg1, Tail1... >, List< Arg2, Tail2... > > {
    enum { value = ArgsCompat< Arg1, Arg2 >::value && Test< List< Tail1... >, List< Tail2... > >::value };
  };

namespace Test {
  template< typename... >
  struct List{};

  template< typename H, typename... T >
  struct List< H, T... >{
    typedef H Head;
    typedef List< T... > Tail;
  };

  template< typename T > struct RemoveRef { typedef T type; };
  template< typename T > struct RemoveRef< T& > { typedef T type; };

  template< typename Arg1, typename Arg2 >
  struct ArgsCompat {
    static int test( const typename RemoveRef< Arg2 >::type & );
    enum { value = true };
  };

  template< typename Elem1, typename Elem2 > struct Test { enum { value = false}; };
  template<> struct Test< List<>, List<> > { enum { value = true }; };
  template< typename Arg1> struct Test< Arg1, List<> > { enum { value = true }; };
  template< typename Arg1, typename Arg2, typename... Tail1, typename... Tail2 >
  struct Test< List< Arg1, Tail1... >, List< Arg2, Tail2... > > {
    enum { value = ArgsCompat< Arg1, Arg2 >::value && Test< List< Tail1... >, List< Tail2... > >::value };
  };

}

int main( void ) {
  static_assert( Test::Test< Test::List<>, Test::List<> >::value, "" );
  static_assert( Test::Test< Test::List< char >, Test::List< char > >::value, "" );
  static_assert( Test::Test< Test::List< char, int, double >, Test::List< char, int, double > >::value, "expected true" );
  return 0;
}```

CodePudding user response:

When it comes to the instantiations in the two last static_asserts, they both match these:

template <typename Elem1, typename Elem2> struct Test { enum { value = false }; };
template <typename Arg1, typename Arg2, typename... Tail1, typename... Tail2>
struct Test<List<Arg1, Tail1...>, List<Arg2, Tail2...> > { ... };

When there's more than one template matching, it always selects the one that is more constrained, which is the second one in this case. The first one is more relaxed (Elem1 and Elem2 can be anything). If they had been equally constrained there would have been ambiguity and compilation would fail.

The parameters in the first static_assert matches these two:

template <typename Elem1, typename Elem2> struct Test { enum { value = false }; };
template <> struct Test<List<>, List<> > { enum { value = true }; };

and again, the second one is more constrained and is therefore selected.

  • Related