suppose I have these declarations
template<typename T> class User;
template<typename T> class Data;
and want to implement User<> for T = Data<some_type> and any class derived from Data<some_type> but also allow for other specialisations defined elsewhere.
If I didn't already have the declaration of the class template User<>, I could simply
template<typename T,
typename A= typename std::enable_if<is_Data<T>::value>::type>
class User { /*...*/ };
where
template<template<typename> data>> struct is_Data
{ static const bool value = /* some magic here (not the question) */; };
However, this has two template parameters and thus clashes with the previous declaration, where User<> is declared with only one template parameter. Is there anything else I can do?
(Note
template<typename T,
typename A= typename std::enable_if<is_Data<T>::value>::type>
class User<T> { /*...*/ };
doesn't work (default template arguments may not be used in partial specializations),
nor does
template<typename T> class User<Data<T>> { /*...*/ };
as it doesn't allow types derived from Data<>, neither does
template<typename T>
class User<typename std::enable_if<is_Data<T>::value,T>::type>
{ /*...*/ };
since template parameter T is not used in partial specialization.)
IF the original declaration of User<> can be adapted to
template<typename, typename=std::true_type> class User;
then we can find a solution (following Luc Danton's comment, instead of using std::enable_if)
template<typename>
struct is_Data : std::false_type {};
template<typename T>
struct is_Data<Data<T>> : std::true_type {};
template<typename T>
class User<T, typename is_Data<T>::type >
{ /* ... */ };
However, this doesn't answer the original question, since it requires to change the original definition of User. I'm still waiting for a better answer. This could be one that conclusively demonstrates that no other solution is possible.
Since you said you were still waiting for a better answer, here's my take on it. It's not perfect, but I think it gets you as far as possible using SFINAE and partial specializations. (I guess Concepts will provide a complete and elegant solution, but we'll have to wait a bit longer for that.)
The solution relies on a feature of alias templates that was specified only recently, in the standard working drafts after the final version of C++14, but has been supported by implementations for a while. The relevant wording in draft N4527 [14.5.7p3] is:
However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id. [ Example:
template<typename...> using void_t = void;
template<typename T> void_t<typename T::foo> f();
f<int>(); // error, int does not have a nested type foo
—end example ]
Here's a complete example implementing this idea:
#include <iostream>
#include <type_traits>
#include <utility>
template<typename> struct User { static void f() { std::cout << "primary\n"; } };
template<typename> struct Data { };
template<typename T, typename U> struct Derived1 : Data<T*> { };
template<typename> struct Derived2 : Data<double> { };
struct DD : Data<int> { };
template<typename T> void take_data(Data<T>&&);
template<typename T, typename = decltype(take_data(std::declval<T>()))>
using enable_if_data = T;
template<template<typename...> class TT, typename... Ts>
struct User<enable_if_data<TT<Ts...>>>
{
static void f() { std::cout << "partial specialization for Data\n"; }
};
template<typename> struct Other { };
template<typename T> struct User<Other<T>>
{
static void f() { std::cout << "partial specialization for Other\n"; }
};
int main()
{
User<int>::f();
User<Data<int>>::f();
User<Derived1<int, long>>::f();
User<Derived2<char>>::f();
User<DD>::f();
User<Other<int>>::f();
}
Running it prints:
primary
partial specialization for Data
partial specialization for Data
partial specialization for Data
primary
partial specialization for Other
As you can see, there's a wrinkle: the partial specialization isn't selected for DD, and it can't be, because of the way we declared it. So, why don't we just say
template<typename T> struct User<enable_if_data<T>>
and allow it to match DD as well? This actually works in GCC, but is correctly rejected by Clang and MSVC because of [14.5.5p8.3, 8.4] ([p8.3] may disappear in the future, as it's redundant - CWG 2033):
The argument list of the specialization shall not be identical to
the implicit argument list of the primary template.
The specialization shall be more specialized than the primary template (14.5.5.2).
User<enable_if_data<T>> is equivalent to User<T> (modulo substitution into that default argument, which is handled separately, as explained by the first quote above), thus an invalid form of partial specialization. Unfortunately, matching things like DD would require, in general, a partial specialization argument of the form T - there's no other form it can have and still match every case. So, I'm afraid we can conclusively say that this part cannot be solved within the given constraints. (There's Core issue 1980, which hints at some possible future rules regarding the use of template aliases, but I doubt they'll make our case valid.)
As long as the classes derived from Data<T> are themselves template specializations, further constraining them using the technique above will work, so hopefully this will be of some use to you.
Compiler support (this is what I tested, other versions may work as well):
Clang 3.3 - 3.6.0, with -Wall -Wextra -std=c++11 -pedantic - works as described above.
GCC 4.7.3 - 4.9.2, same options - same as above. Curiously, GCC 5.1.0 - 5.2.0 no longer selects the partial specialization using the correct version of the code. This looks like a regression. I don't have time to put together a proper bug report; feel free to do it if you want. The problem seems to be related to the use of parameter packs together with a template template parameter. Anyway, GCC accepts the incorrect version using enable_if_data<T>, so that can be a temporary solution.
MSVC: Visual C++ 2015, with /W4, works as described above. Older versions don't like the decltype in the default argument, but the technique itself still works - replacing the default argument with another way of expressing the constraint makes it work on 2013 Update 4.
As you only want to implement it when a single condition is true, the easiest solution is to use a static assertion. It does not require SFINAE, gives a clear compile error if used incorrectly and the declaration of User<> does not need to be adapted:
template<typename T> class User {
static_assert(is_Data<T>::value, "T is not (a subclass of) Data<>");
/** Implementation. **/
};
See also: When to use static_assert instead of SFINAE?. The static_assert is a c++11 construct, however there are plenty workarounds available for pre-c++11 compilers, like:
#define STATIC_ASSERT(consdition,name) \
typedef char[(condition)?1:-1] STATIC_ASSERT_ ## name
If the declaration of user<> can be changed and you want two implementations depending on the value of is_Data, then there is also a solution that does not use SFINAE:
template<typename T, bool D=is_Data<T>::value> class User;
template<typename T> class User<T, true> {
static_assert(is_Data<T>::value, "T is not (a subclass of) Data<>"); // Optional
/* Data implementation */
};
template<typename T> class User<T, false> {
static_assert(!is_Data<T>::value, "T is (a subclass of) Data<>"); // Optional
/* Non-data implementation */
};
The static assertions only checks whether the user did not accidentally specify the template argument D incorrectly. If D is not specified explicitly, then the static assertions can be omitted.
Related
Imagine I have a concept that checks for some trait of a member type, e.g. its integralness. In anticipation of what is to come, let me define it in two ways:
namespace decayedAtConcept {
template <typename T>
concept hasIntegralMemberType = std::integral<typename std::decay_t<T>::MemberType>;
}
namespace notDecayedAtConcept {
template <typename T>
concept hasIntegralMemberType = std::integral<typename T::MemberType>;
}
With the following definitions
struct HasIntegralMemberType {using MemberType = int;};
struct HasNonintegralMemberType {using MemberType = double;};
struct DoesNotHaveMemberType {};
and the following function declarations
void usesIntegralMemberType1 (decayedAtConcept::hasIntegralMemberType auto&&);
void usesIntegralMemberType2 (notDecayedAtConcept::hasIntegralMemberType auto&&);
template <typename T> requires notDecayedAtConcept::hasIntegralMemberType<std::decay_t<T>>
void usesIntegralMemberType3 (T&&);
in effect, a client can try to use this library in the following way:
void client () {
HasIntegralMemberType h;
usesIntegralMemberType1(HasIntegralMemberType{});
usesIntegralMemberType1(h); // OK
//usesIntegralMemberType1(HasNonintegralMemberType{}); //note: constraints not satisfied
//usesIntegralMemberType1(DoesNotHaveMemberType{}); //note: constraints not satisfied
usesIntegralMemberType2(HasIntegralMemberType{});
//usesIntegralMemberType2(h); //error: ‘HasIntegralMemberType&’ is not a class, struct, or union type
usesIntegralMemberType3(HasIntegralMemberType{});
usesIntegralMemberType3(h); // OK again
}
The notes just demonstrate that the concept works as expected in usesIntegralMemberType1. My question is related to the error caused by the naive usage of the notDecayedAtConcept in usesIntegralMemberType2.
Apparently, the notDecayedAtConcept version of the concept is less convenient to use because any time it is used with a universal reference, it needs to be decayed in a requires clause as in usesIntegralMemberType3. We even lose the syntactic sugar we had in defining the constrained template parameter in usesIntegralMemberType1.
And now the question: are there already some guidelines which of the two strategies to use in such a situation? Or, is there any other, more transparent/convenient solution?
I have recently updated gcc compiler from version 5 to 8, and it has broken our production code. A simplified version of the broken code is included below:
#include <utility>
// Imagine this has several template parameters not just Id and
// this class provides lots of friend functions for retrieving
// all this "metadata". Just one is implemented in this example.
template <typename Tag, unsigned Id>
class MetadataImpl
{
template <typename T, typename U>
using matches =
typename std::enable_if<std::is_same<T, U>::value>::type;
template <typename _Tag, typename = matches<_Tag, Tag>>
friend unsigned GetId(Tag* = nullptr)
{ return Id; }
};
// Let's generate some instances...
template class MetadataImpl<int, 1>;
template class MetadataImpl<double, 2>;
// And a simple test function.
unsigned test()
{
return GetId<int>();
}
In simplest terms, this code provides a way of capturing metadata around a tag (a type in the example above, but could also be an enum value) and was originally coded some 10+ years ago and has seen many gcc upgrades, but something "broke" in gcc 6 (verified via the famous godbolt online compiler).
It is quite possible that this code wasn't supported by the c++ standard and was just a gcc extension which has now been dropped, but I would be interested to know if this was actually the case and what the rationale might be for it being rejected by the standard.
It seems also that clang doesn't support this code either but I have noticed that if you do an ast-dump (clang -Xclang -ast-dump) that clang does at least hold the definitions of these friend functions, but it seems it is unable to find them when used (a template argument deduction failure?).
I would be very delighted to know of any work-around or alternative that works in as similar a way as possible, i.e. though some form of single line instantiation and, critically, only for tags that have been explicitly instantiated.
Specifically, what I don't want is to have a string of template functions that all have to be implemented per tag (I've just shown one metadata item and there are many in the production code, some of which derive further information from combinations of template arguments and/or other type information). The original solution developed above led to very clean, extendable and maintainable code. Wrapping it all in some complex macro would be the absolute worst-case scenario!
There is a similar question and answer here but I can't see how to make this solution work in this scenario since the argument to the friend function is not the parent class itself, but a template argument of it.
Changing the GetId function to take MetadataImpl<...> as its argument would not be a viable solution, since then the use of the functions becomes utterly impractical. The places where the functions are called from just want to provide the tag itself.
Thank you, in advance, for any help!
The reason it worked before is because gcc has bugs. It wasn't standard C++ and most probably won't ever be. But this is
namespace
{
template<typename T>
struct flag
{
friend constexpr unsigned adl(flag<T>);
};
template <typename T, unsigned n>
class meta
{
friend constexpr unsigned adl(flag<T>)
{
return n;
}
};
template<typename T>
constexpr auto getId()
{
return adl(flag<T>{});
}
}
And you get to write the exact same thing as before
template class meta<int, 1>;
template class meta<double, 2>;
auto foo()
{
return getId<int>();
}
Note the anonymous namespace, you run afoul the ODR if you don't have it.
Why don't you just write GetId as a free function and specialize it as needed?
template <typename Tag>
unsigned GetId()
{
return /* default value */;
}
template <> unsigned GetId<int> () { return 1; }
template <> unsigned GetId<double>() { return 2; }
// ...
A regex replace can help you with transforming the class template explicit instantiations to these function template specializations. (This is one of the few circumstances under which specializing a function template would make sense.)
If you don't want a default value, just define the primary function as = delete: (C++11)
template <typename Tag>
unsigned GetId() = delete;
If you can use variable templates, (C++14) you can make the code look prettier:
template <typename Tag>
unsigned Id = /* default value */;
template <> unsigned Id<int> = 1;
template <> unsigned Id<double> = 2;
// ...
So maybe this violates your "no strings of templates" requirement but you can use a tag helper struct:
template <typename T> struct tag {};
template <> struct tag<int> {
static constexpr unsigned Id = 1;
// any more customization points here
};
template <> struct tag<double> {
static constexpr unsigned Id = 2;
};
(That would also avoid the many explicit instantiations). The metadata implementation would be:
template <typename Tag>
class MetadataImpl
{
friend unsigned GetId(MetadataImpl)
{ return Tag::Id; }
};
and now you can write a helper to ADL call GetId.
template <typename T>
unsigned GetId() {
return GetId(MetadataImpl<tag<T>>());
}
Demo.
What's the idiomatic way for constraining a type template parameter so that it only accepts instantiations of a specific template?
e.g.,
template<typename P>
class C {
C() = default;
...
};
template<typename T>
class Accepted {
...
};
template<typename T>
class Other {
...
};
C<Accepted<float>> obj1; // should compile
C<Accepted<int>> obj2; // should compile
C<Other<int>> obj3; // should not compile
C<double> obj4; // should not compile
Specialization is the answer.
template<typename P> class C;
template<typename T>
class Accepted {
...
};
template<typename P>
class C<Accepted<P>> {
C() = default;
...
};
The above makes any C<Accepted<T>> be well-formed because it chooses the specialization when instantiating. While any sort of other C<T> chooses the primary specialization, which is not defined, and so will not compile.
I see two possible answers without knowing more about the context.
template<typename T> class C;
template<template<typename> typename TT, typename T> class C<TT<T>> {
... static_assert(std::is_same<TT<T>, Accepted<T>>::value, "The given type is not an instantiation of Accepted!"); ...
}
This is only so helpful - what is so special about Accepted? You may have a second template, e.g. SortedAccepted<T>, that meets the same requirements, but you've set out to constrain the template argument to Accepted<T>.
One option is to design Accepted as a contract so that derivations from it must satisfy the same constraints as Accepted itself:
template<template<typename> typename TT, typename T> class C {
... static_assert(std::is_base_of<Accepted<T>, TT<T>>::value,
"The given type is not a derivation of Accepted!"); ...
};
This follows from the design principle that Accepted should be extended, but not modified, by its derivations. From the previous example, maybe Accepted is required to have an idempotent 'sort' method -- accepted.sort().sort() == accepted.sort() -- whereas SortedAccepted implicitly retains this property, but additionally provides sorted_accepted.sort() == sorted_accepted. You have nothing to lose in allowing a SortedAccepted wherever you expect an Accepted, but plenty to gain!
If I understood your problem correctly, you might be interested in Traits and Concepts.
Type traits as seen in <type_traits> and Boost's extensions are reified contracts, providing compile-time guarantees about collections sharing traits, expressions of these, and so on. For example, S s; T t; auto u = s + t where std::is_arithmetic<S>::value and std::is_arithmetic<T>::value are true, guarantees that std::is_arithmetic<decltype(u)>::value is true; if either is_floating_point, so is u.
Concepts are the next logical step from type traits: essentially, the reified requirement of traits. (See: 'Concepts Lite')
I have a templated matrix class that I explicitly instantiate for various POD types and custom class types. Some of the member functions however don't make sense for a few of such custom types. For example:
Matrix<int> LoadFile(....); // This makes sense
Matrix<My_custom_class> LoadFile(...); //This doesn't make sense in the context of the custom class
Can I prevent the instantiation of the LoadFile function (which is a member function) for Matrix objects of select types? So far I have avoided the issue by making LoadFile a friend function and then explicitly controlling its instantiation. But I want to know if I can do this when LoadFile is a member function of Matrix.
The first question is whether you really need to control this. What happens if they call that member function on a matrix that stores My_custom_class? Can you provide support in your class (or the template) so that the member function will work?
If you really want to inhibit the use of those member functions for some particular type, then you can use specialization to block the particular instantiation:
template <typename T>
struct test {
void foo() {}
};
template <>
inline void test<int>::foo() = delete;
Or even just add static_asserts to the common implementation verifying the preconditions for what types is it allowed or disallowed?
template <typename T>
struct test {
void foo() {
static_assert(std::is_same<T,int>::value || std::is_same<T,double>::value,
"Only allowed for int and double");
// regular code
}
};
with std::enable_if, this is the best I can come up with
template< typename T >
struct Matrix {
template< typename T >
Matrix< typename std::enable_if<std::is_integral<T>::value, T>::type >
LoadFile()
{
return Matrix<T>();
}
};
Matrix<int> a;
Matrix<int> b = a.LoadFile<int>()
only type int compile while other don't.
Can I prevent the instantiation of the LoadFile function (which is a member function) for Matrix objects of select types?
Your best bet here would be to use a static_assert that would create a compiler error when you attempt to call the method in a version of the class instantiated with a blocked type. Using std::enable_if, and other methods that would selectively "disable" a method itself would require you to create partial or full specializations of the class with and without the methods in question in order to prevent compiler errors. For instance, AFAIK, you cannot do the following:
template <typename T>
struct test
{
static const bool value = false;
};
template<>
struct test<double>
{
static const bool value = true;
};
template<typename T>
struct example
{
void print() { cout << "Printing value from print()" << endl; }
typename enable_if<test<T>::value, T>::type another_print()
{
cout << "Printing value from another_print()" << endl;
return T();
}
};
If you attempted to instantiate an example<int>, etc., you would end up with a compiler error at the point of instantiation of the object type. You couldn't simply call example<int>::print() and be okay, and only run into a problem if you chose to call example<int>::another_print(). Specializations of example<T> could get you around the issue, but that can be a bit of a mess. As originally surmised, a static_assert would probably be the easiest case to handle, along with a nice message to the end-user explaining what went wrong.
Keep in mind that creating compiler errors is the goal, and it's a good one to have. If you blocked a method from being instantiated, and the end-user decided to invoke it, you'd end up with a compiler error either way. The version without the static_assert will leave a lot of head-scratching as the user of your class attempts to parse a probably very verbose compiler error message, where-as the static_assert method is direct and to the point.
If the selected set of types is known at compile time, and you are using c++11 with a compiler that supports type aliases, uniform initialization and constexpr (for example gcc 4.7) you can make your code a bit cleaner like this (from previous example above by yngum):
template <bool Cond, class T = void>
using enable_if_t = typename std::enable_if<Cond, T>::type;
template< typename T >
struct Matrix {
template< typename T >
//std::is_integral has constexpr operator value_type() in c++11. This will work thanks to uniform init + constexpr. With the alias, no more need for typename + ::type
Matrix<enable_if_t<std::is_integral<T>{}>>
LoadFile()
{
return Matrix<T>();
}
};
Matrix<int> a;
Matrix<int> b = a.LoadFile<int>();
Beware of compatibility of this code, though, because these features have been only recently supported and some compilers don't do yet. You can see more about c++11 compiler support here.
If you could use the TypeLists from the ( http://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315 ) - Loki you could implement something like:
template<bool>
struct Static_Assert;
template<>
struct Static_Assert<true>{};
class B{};
template<typename T>
class A{
public:
A(){
Static_Assert< 0 == utils::HasType<T, TYPELIST_2(B,int) >::value >();
}
};
Then your HasType would be something like:
template<typename T, typename TList>
struct HasType{
enum { value = 0+HasType< T, typename TList::Tail >::value };
};
template<typename T>
struct HasType< T, NullType >{
enum { value = 0 };
};
template<typename T, typename U>
struct HasType< T, TypeList<T, U> >{
enum { value = 1 };
};
In the list you can add the classes which you would like prevent to be passed as the template parameters.
Would any one knows according to what rules code below doesn't compile?
template <class T>
struct B
{
typedef T type;
};
template<class T>
struct X
{
};
template<class T>
struct X<B<T>::type*>//HERE I'M PARTIALLY SPECIALIZING (WELL, TRYING TO...)
{
};
Please see comment inside the code.
How do you think that will work? The compiler will look to see if there is a class T somewhere that has a typedef "type" to your class?
It just won't. Even though it's a pointer.
Remember that presumably your B template is presumably specialised in places so that type is not always T*, but it can't deduce it with reverse engineering.
For those who did not understand my answer fully, what you are asking the compiler to do is find a class U such that B::type is the class you pass in as a parameter.
class Foo;
class Bar;
template<> struct B<Foo>
{
typedef int type;
};
template<> struct B<Bar>
{
typedef int type;
};
X<int*> // ambiguous, T is Foo or Bar?
It is difficult to know exactly why you are trying to do what you are. You can do a partial specialization on all pointers and then a total specialization on specific pointers, which could be implement in terms of another template.
You need to use typename keyword as,
template<class T>
struct X<typename B<T>::type*>
{
};
It's because B<T>::type is a dependent name. So typename is required!
--
EDIT:
Even after putting typename, it isn't compiling. I think it's because deduction of type T in B<T> from X<U> is difficult, or possibly impossible, for the compiler. So I believe its non-deduced context.
See a similar example here and the discussion:
Template parameters in non-deduced contexts in partial specializations
However, if you change the specialization to this:
template<class T>
struct X<B<T> >
{
};
Then it becomes the deducible context, and so would compile.
Assuming you already added typename as suggested by Nawaz.
The problem is exactly explained in the error message you encounter: "template parameter is not deducible in partial specialization B<T>::type*. The problem is that B<T>::type and T is exactly the same for all types T. Consider the following example:
class MyClass1 {};
typedef typename B<MyClass>::type MyClass2; //(*)
X<MyClass*> obj1;
X<MyClass2*> obj2;
The result of line (*) is a type MyClass2 which is essentially MyClass1. So, obj1 and obj2 should be objects of the same class. Now, which version of template X should they use?
If you would expect the specialised version of X, tell me if the answer should be the same if line (*) is removed (and obviously obj2 as well). Still obj1 should be the specialised version of X as line (*) has nothing to do with it.
But now we expect the compiler to detect that some type can be potentially declared as B<T>::type although we never do this. We expect the compiler to verify all possible template instantiations to check if there is no strange typedef in one of them.
I hope this clarifies why such specialisation cannot be handled by the compiler.
An alternative that might help
I believe your problem could be attacked by creating a trait class for explicitly marking types that should be handled in a special way. Something like this:
template <bool v>
struct boolean_value {
static const bool value=v;
};
template <typename T>
struct is_my_interesting_type : public boolean_value<false> {};
class MyClass {
...
};
template <>
struct is_my_interesting_type<MyClass> : public boolean_value<true> {};
template <typename T, bool special>
class InternalX {
... //generic version of your template X
};
template <typename T>
class InternalX<T,true> {
... //special version of your template X
};
template <typename T>
class X : public InternalX<T,is_my_interesting_type<T>::value> {};
Also, you might be interesting how it is done in boost library, in particular Boost.Type_Traits
"The argument list cannot be identical to the non-specialized argument list (it must specialize something)"
see partial_specialization at en.cppreference.com