Can I specify exactly what kind of arguments a template can receive? For example, I'd like to create a template that can only be instantiated with classes that are or extend class A. In Java, generics support this with:
class B<T extends A> { }
Can something similar be achieved with templates in C++?
template <typename T (?)> class B { }
There are two ways to do this.
First, through a hidden dummy template parameter that uses std::enable_if with a std::is_base_of<A, T>::value as condition. If the latter expression evaulates to false, then the nested type does not exist in std::enable_if. If you were using this on overloaded functions, SFINAE then means "substitution failure is not an error", and the overload in question would be removed from the set of viable functions. HOwever in this situation, there is no other class template to match your call, and then you do get a compile-time error.
SFINAE is a very subtle mechanism and easy to get wrong. E.g. if you have multiple class specializations with different SFINAE conditions, you have to make sure that they are all non-overlapping, or else you get an ambiguity.
Second, you can do a simple static_assert with a std::is_base_of<A,T>::value inside the body of the class. The advantage of this method is that you also specify a more readable error message compared to the SFINAE method. A disadvantage is that you always get an error, and you cannot silently suppress this particular template and select another one. But overall I think this method is recommended in your case.
#include<type_traits>
class A {};
class C: public A {};
class D {};
// first alternative: SFINAE on hidden template parameter
template
<
typename T,
typename /* dummy */ = typename std::enable_if<
std::is_base_of<A, T>::value
>::type
>
class B
{
};
// second alternative: static_assert inside class
template
<
typename T
>
class E
{
static_assert(std::is_base_of<A, T>::value, "A should be a base of T");
};
int main()
{
B<A> b1;
B<C> c1;
//B<D> d1; // uncomment this line to get a compile-time error
E<A> b2;
E<C> c2;
//E<D> d2; // uncomment this line to get a compile-time error
return 0;
}
As was pointed out in the comments, you can use either a decent C++11 compiler (VC++ 2010 or later, gcc 4.5 or later) or the Boost or TR1 libraries to get the <type_traits> functionality. Note however that the std::is_base_of<A, A>::value evaluates to true, but the old boost::is_base_of<A, A>::value used to evalute to false.
You can do this with static_assert and is_base_of:
#include <type_traits>
template<typename T> class D {
static_assert(std::is_base_of<A, T>::value, "must be derived from A");
};
Or you can use enable_if:
#include <type_traits>
template<typename T, typename = void> class D;
template<typename T> class D<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> {
};
For C++03 you can use boost; is_base_of from Boost.TypeTraits, static_assert from Boost.StaticAssert, enable_if from Boost.EnableIf.
Related
When using template like this:
class A {…}
class B : A {…}
class C : A {…}
template<typename T>
class D{…}
I need T can only be B or C. Which means T must be a derivation of A.
Is there any way to do this? Thanks!
Use std::is_base_of along with std::enable_if:
template<typename T, typename X = std::enable_if<std::is_base_of<A, T>::value>::type>
class D{...}
Note that it will accept any T as long as it derives from A. If you need T to be either B or C, then you need to modify it, and use std::is_same or/and std::conditional along with std::enable_if.
You could make it clean as:
template<typename T, typename Unused = extends<T,A>>
class D{...}
where extends is defined as:
template<typename D, typename B>
using extends = typename std::enable_if<std::is_base_of<B,D>::value>::type;
static_assert can also be used (like other answers have shown) if you want it to result in error and compilation failure. However if you need selection or deselection, say from many specializations, then use the above approach.
Hope that helps.
You can use static_assert in combination with std::is_base_of:
#include <type_traits>
class A {};
class B : A {};
class C : A {};
class X{};
template<typename T>
class D
{
static_assert(std::is_base_of<A,T>::value, "T must be derived from A");
};
int main()
{
D<C> d_valid;
D<X> d_fails; // compilation fails
return 0;
}
live on ideone
Yes, this should do it:
template<typename T>
class D {
static_assert(std::is_base_of<A,T>::value, "not derived from A");
// ...
};
Demo here.
But this is not the idea behind templates. If you write templated code, then it should be generic, I.e. work for all types that support the operations that you apply on them.
I'm trying to build a trait that checks for the existence of a nested, templated class. This is my take for checking whether a class O has a nested class inner with template parameter T:
template <typename O, typename T> struct has_inner {
static const bool value = std::is_class<typename O::template inner<T> >::value;
};
However, this does not work properly. Given two example classes dummy and ok
struct dummy {};
struct ok {
template <typename T>
struct inner {
};
};
The check on ok
std::cout << std::boolalpha << has_inner<ok, float>::value << std::endl;
will work, whereas the check on dummy
std::cout << std::boolalpha << has_inner<dummy, int>::value << std::endl;
will fail to compile on clang 3.2 with the error
error: 'inner' following the 'template' keyword does not refer to a template
static const bool value = std::is_class<typename O::template inner<T> >::value;
^~~~~
note: in instantiation of template class 'has_inner<dummy, int>' requested here
std::cout << std::boolalpha << has_inner<dummy, int>::value << std::endl;
It appears that the compiler tries to actually form that templated expression prior to passing it on to std::is_class. Consequently I see two solutions:
Tell the compiler to delay the template expansion, or
Use a different approach altogether.
However, I don't know how to perform either, can anyone help?
THE PROBLEM
You normally implement traits like this using, and relying on, SFINAE, something which your implementation doesn't take advantage of.
As stated the compiler will try to instantiate typename O::template inner<T>, no matter if it's possible or not; and if it isn't possible the compiler will throw an error diagnostic in your face.
What you need to do is a conditional check to see if T actually has a template-class inside it, without instantiating it if it hasn't.
THE SOLUTION - SFINAE TO THE RESCUE!
An implementation might look like the below snippet, an explanation will follow.
namespace impl {
template<class T, class... Args>
struct has_inner {
template<class U, typename = typename U::template inner<Args...>> // (A)
static std::true_type test (int);
template<class U>
static std::false_type test (...); // (B)
using result_type = decltype (test<T> (0)); // (C)
};
}
template<class... Ts>
using has_inner = typename impl::has_inner<Ts...>::result_type;
Note: by using decltype(test<T>(0)) we will have either std::true_type, or std::false_type which are both the standard behavior when dealing with results from type-traits.
The rules of SFINAE states that if a function template would yield an invalid function declaration upon instantiation, it is as if this function didn't exist, the compiler will try searching for another match, instead of giving up.
This is what happens at (C), we try to call (A) but if that fails (ie. an invalid expression is yield inside template<class U, typename ...> we will end up calling (B).
(B) isn't as good match as a successful instantiation of (A), but if (A) can't be instantiated.. (B) will do.
You need to use a trait class and SFINAE, something like this:
template<class A, typename B>
struct has_inner
{
private:
template<class T, typename U>
static std::true_type has(typename T::template inner<U>*);
template<class, typename>
static std::false_type has(...);
public:
static constexpr auto value = decltype(has<A, B>(nullptr))::value;
};
Now you can use it with correct results:
static_assert(has_inner<ok, float>::value, "ok does not have inner"); // OK
static_assert(has_inner<dummy, float>::value, "dummy does not have inner"); // ERROR
Consider a template class:
template <class First, class Second, class Third, class Fourth>
class MyClass;
What is the right way to add a member function for certain sets of template parameters?
For example, how to add a member f(), when Second is a std::string() ?
Here is the method I've found and I traditionally use:
#include <iostream>
#include <type_traits>
#include <array>
template <class Container>
struct Array
{
Container data;
template <class... Dummy,
class = typename std::enable_if<sizeof...(Dummy) == 0>::type,
class = typename std::enable_if<
std::tuple_size<
typename std::conditional<sizeof...(Dummy),
Container,
Container
>::type
>::value == 1
>::type
>
inline typename Container::value_type& value(Dummy...)
{return data[0];}
};
int main()
{
Array<std::array<double, 0>> array0; // Does not have the value() member
Array<std::array<double, 1>> array1; // Have the value() member
Array<std::array<double, 2>> array2; // Does not have the value() member
Array<std::array<double, 3>> array3; // Does not have the value() member
}
It works well, but it's more a metaprogramming trick than a clean/standard way to do it.
You may use inheritance and specialization.
Something like:
template <typename T> struct Helper2 {};
template <> struct Helper2<std::string>
{
void f() {};
};
template <class First, class Second, class Third, class Fourth>
struct MyClass : public Helper2<Second>
{
// Normal code.
};
int main()
{
MyClass<std::string, int, std::string, std::string> c1;
MyClass<int, std::string, int, int> c2;
//c1.f(); // this is an error
c2.f(); // this works
return 0;
}
I don't quite get the purpose of Dummy in your declaration. It can be done with two defaulted template parameters that are not used at all in function parameter list:
#include <type_traits>
#include <string>
template <class First> // arguments ommited for brevity
struct MyClass {
template<
typename U = First,
typename = typename std::enable_if< std::is_same<U, std::string>::value >::type
>
void f() {}
};
int main()
{
MyClass<int> c1;
MyClass<std::string> c2;
// this is an error
// c1.f();
c2.f(); // this works
}
Live example.
Note that it's possible to cheat: c1.f<std::string>(); will still work.
In C++1y concepts TS we have requires clauses that may let you do this easily. See http://isocpp.org/files/papers/n3929.pdf -- I may be wrong, however.
Outside of C++1y, your technique makes your program ill-formed with no diagnosis required, as all function templates must have at least one valid specialization. As it happens, this is a very rarely enforced requirement, because solving for "there are no valid specializations" in the general case involves solving the halting problem. Despite this, it is the programmers responsibility to ensure that all template functions have at least one valid set of template arguments.
The way I have found that is strictly legal for a zero-argument function in C++11 is to use CRTP based class specialization that eliminates the method in a specialization of the CRTP base.
Another way is to create a private, inaccessible type, and make that the only way to create a legal specialization in the case where you want to disable it. Then privately inside the class you can cheat, but outside you cannot cheat.
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.
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.