基于虚方法存在性为接口条件性应用Mixin的元编程方案
Alright, let's tackle this problem properly. The key here is to let the compiler itself validate whether your Mixin can legally override the target method in the base class—no manual trait checks with duplicate signatures needed. Here's a clean solution that leverages SFINAE and the override keyword's built-in validation:
Step 1: Keep Your Mixin as-Is
First, retain your original Mixin template; the override keyword here is critical—it will automatically fail compilation if the base class doesn't have a matching virtual method:
template <class T> struct Mixin : public T { void f() override {} // `override` enforces matching virtual method in T };
Step 2: Create a Trait to Validate Mixin Applicability
We'll make a trait that checks if instantiating Mixin<T> is valid. This works because if T lacks the required virtual f(), the override keyword will cause a substitution failure, which SFINAE will catch:
#include <type_traits> // Fallback: Mixin isn't applicable template <template <class> class Mixin, class T, class = void> struct is_mixin_applicable : std::false_type {}; // Specialization: Only valid if Mixin<T> can be instantiated template <template <class> class Mixin, class T> struct is_mixin_applicable<Mixin, T, std::void_t<decltype(sizeof(Mixin<T>))>> : std::true_type {}; // If you're on C++14 or earlier, define void_t yourself: // template <class...> using void_t = void;
Step 3: Implement the Magic Alias
Now use std::conditional_t to select either Mixin<T> or T based on our trait:
template <template <class> class Mixin, class T> using Magic = std::conditional_t<is_mixin_applicable<Mixin, T>::value, Mixin<T>, T>;
Test It Out
Let's verify with your example interfaces:
struct Intf1 { virtual void f() = 0; }; struct Intf2 {}; // Compile-time checks to confirm behavior static_assert(std::is_same_v<Magic<Mixin, Intf1>, Mixin<Intf1>>, "Mixin should apply to Intf1"); static_assert(std::is_same_v<Magic<Mixin, Intf2>, Intf2>, "Mixin should NOT apply to Intf2");
Why This Is Better Than Manual Traits
- No duplicate signatures: We don't have to redefine the
f()signature in a trait—we reuse the exact signature from your Mixin, eliminating the risk of mismatches. - Enforces virtual method matching: The
overridekeyword ensures the base class has a virtual method with an exact matching signature (including const/volatile qualifiers, ref-qualifiers, and return type covariance). - Handles edge cases automatically: All the nuance of C++ override rules are handled by the compiler, not your manual trait code.
Note for Non-Default-Constructible Mixins
If your Mixin doesn't have a default constructor, replace decltype(sizeof(Mixin<T>)) with any expression that triggers template instantiation. For example:
template <template <class> class Mixin, class T> struct is_mixin_applicable<Mixin, T, std::void_t<Mixin<T>>> : std::true_type {};
内容的提问来源于stack exchange,提问作者Markus Mayr

