I have a structure like:
template <typename Arg1, typename Arg2>
class TemplateClass { ... };
template <typename TClass>
class UsesTemplateClass {
public:
UsesTemplateClass( TClass& instance ) inst{instance} { ... }
private:
TClass& inst;
};
template <typename TClass>
auto make_uses_template_class( TClass& instance ) {
return UsesTemplateClass<TClass>{ instance };
}
The reason make_uses_template_class exists is that the function template can deduce the type, so the user client doesn't have to explicitly specify it. I realize that C++17 has CTAD to better solve this.
This works just fine as long as the type passed into make_uses_template_class really is a specialization of TemplateClass, but if it's not, the result will be some error in UsesTemplateClass.
I would like to make sure that a make_template_class overload doesn't exist if TClass isn't a TemplateClass. Also, I would like the error message to be reasonable.
I know there are several ways to do this, but I don't see a lot of consistent guidance of how to use enablers or static_asserts in this sort of situation.
For instance, regarding the class, I thought I could do something like:
template <typename TClass>
class UsesTemplateClass; // declared but not defined
template <typename Arg1, typename Arg2>
class UsesTemplateClass<Arg1, Arg2> {
// real definition
};
which would work (if you instantiated it with anything other than a TemplateClass, it would complain that UsesTemplateClass<SomeOtherType> doesn't exist). I'm not thrilled that I'd have to explicitly specify the arguments to TemplateClass in my specialization because in the general case, there could be several template arguments that are subject to change.
Alternatively, I had the idea of putting something like using template_class_tag = void in TemplateClass and then defining UsesTemplateClass as:
template <typename TClass,
typename = typename TClass::template_class_tag >
class UsesTemplateClass { ... };
but I see in several threads that using this sort of enabler for classes is generally frowned upon, and static_assert is generally recommended instead. I understand that the general consensus is that the static_assert could give a better error message and that it's not subject to misuse like a user specifying a type for the default template argument. Unfortunately, I don't believe it's possible to write a static assertion for whether the type TClass::template_class_tag exists.
To work around that problem, I thought I could give TemplateClass a non-template base and use a static assertion with std::is_base_of. I think that would work, though it's a bit intrusive (the base class would serve no other purpose).
Is there a generally accepted idiom for restricting a class like UsesTemplateClass in this way?
The function has the same issue, but I know that enablers and such are often used differently in functions than in classes, so I wanted to ask about that as well.
As "R Sahu" already pointed out in his "Approach 1" code example, not sure why "TClass" is any arbitrary type if only specializations of "TemplateClass" are allowed. Why not follow his basic "Approach 1" or similar. If "TClass" must be any arbitrary type though (for whatever reason), then the following code can be used as a more generic alternative to his "Approach 2" code example (TBH I didn't read his code in detail but the following is a generic technique you can use for any template taking type-based template args only - see "IsSpecialization" comments in code below - click here to run it):