Using `void_t` to detect multiple inheritance type repetition errors
c++, c++17, sfinae, templates, variadic-functions
Solution
As @Canoninos noted, the problem is that:
it isn't the declaration of `dup_helper<T, T>` which causes an error but its definition [...].
Or, in Standardese, the error occurs outside the "immediate context" ([temp.deduct]) of the substitution:
8 - [...] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]
Here the error occurs while instantiating `dup_helper<float, float>` so is not in the "immediate context".
One multiple inheritance trick that's very close to yours involves adding an extra layer of inheritance, by indexing the multiple bases:
helper<<0, 1>, <float, float>>
+
+----+----+
v v
ix<0, float> ix<1, float>
+ +
v v
t<float> t<float>
This gives us a helper class with a valid definition and that can be instantiated but not cast to its ultimate base classes, because of ambiguity:
static_cast<t<float>>(helper<...>{}); // Error, SFINAE-usable
Example.
Problem
I want to implement a `has_no_duplicates<...>` type trait that evaluates to `std::true_type` if the passed variadic type list has no duplicate types. ``` static_assert(has_no_duplicates<int, float>{}, ""); static_assert(!has_no_duplicates<float, float>{}, ""); ``` Let's assume, for the scope of this question, that I want to do that using multiple inheritance. When a class inherits from the same type more than once, an error occurs. ``` template<class T> struct type { }; template<class... Ts> struct dup_helper : type<Ts>... { }; // No errors, compiles properly. dup_helper<int, float> ok{}; // Compile-time error: // base class 'type<float>' specified more than once as a direct base class dup_helper<float, float> error{}; ``` I assumed I could've used `void_t` to "detect" this error, but I couldn't implement a working solution following the code samples from cppreference. This is what I tried: ``` template<class, class = void> struct is_valid : std::false_type { }; // First try: template<class T> struct is_valid<T, std::void_t<decltype(T{})>> : std::true_type { }; // Second try: template<class T> struct is_valid<T, std::void_t<T>> : std::true_type { }; ``` For my third try, I tried delaying the expansion of `dup_helper<...>` using a wrapper class that took `dup_helper` as a template template parameter, like `wrapper<dup_helper, ...>` and expanded it inside of `void_t`. Unfortunately, all my tries resulted in the aforementioned error always preventing compilation. I assume this type of error is not detectable as a "substitution failure", but I'd like confirmation. Is this kind of error actually impossible to detect using `void_t`? (Will it always result in a compilation failure?) Is there a way to detect it without causing compilation to fail? (Or a non-`void_t` workaround that still makes use of the "multiple inheritance trick")?