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.
Related
I am looking for a way to extract the types of an std::tuple to define a method signature. Take the following (contrived) example:
template <typename RetT, typename... ArgsT>
class A
{
public:
typedef RetT ReturnType;
typedef std::tuple<ArgsT...> ArgTypes;
RetT doSomething(ArgsT... args)
{
// Doesn't make much sense, but it's just an example
return (RetT) printf(args...);
}
};
template <typename Enable, typename RetT, typename... ArgsT>
class AAdapter;
// Simply pass arguments along as-is
template <typename RetT, typename... ArgsT>
class AAdapter<std::enable_if_t<!std::is_same_v<RetT, float>>, RetT, ArgsT...> : public A<RetT, ArgsT...> {};
// Add additional first argument if RetT is float
template <typename RetT, typename... ArgsT>
class AAdapter<std::enable_if_t<std::is_same_v<RetT, float>>, RetT, ArgsT...> : public A<RetT, const char*, ArgsT...> {};
template <typename RetT, typename... ArgsT>
class B
{
public:
typedef AAdapter<void, RetT, ArgsT...> AAdapter;
// This needs to have the same method signature (return type and argument types) as AAdapter::doSomething()
template <size_t... Index>
typename AAdapter::ReturnType doSomething (
typename std::tuple_element<Index, typename AAdapter::ArgTypes>::type... args
) {
return a.doSomething(args...);
}
public:
AAdapter a;
};
int main(int argc, char** argv)
{
// I would like to be able to remove the <0,1,2> and <0,1,2,3> below.
B<int, const char*, int, int> b1;
b1.doSomething<0,1,2>("Two values: %d, %d\n", 1, 2);
B<float, const char*, int, int> b2;
b2.doSomething<0,1,2,3>("Three values: %s, %d, %d\n", "a string", 1, 2);
return 0;
}
Consider the way in which AAdapter changes, adds or removes argument types opaque. Basically, I want B::doSomething() to simply redirect to B::AAdapter::doSomething(), so I want both of these methods to have the exact same signature. The question is: How do I get the argument types of B::AAdapter::doSomething() from inside B?
My definition of B::doSomething() in the code above is the furthest I have come: I'm typedef'ing an std::tuple with the argument types inside A, so I can unpack them back to a parameter pack in B. Unfortunately, with the approach above I still need to provide the Index... template parameters manually when calling B::doSomething(). Surely there must be a way to have these Index... parameters automatically deduced from the size of the tuple. I have thought about approaches using std::make_integer_sequence, but that would require me to define an additional method argument for the sequence itself (and it can't be the last argument with a default value because no other arguments are allowed after a parameter pack).
Is there any way I can do this, with or without std::tuple? Solutions that require C++17 will be fine.
EDIT 1:
I realize now that I could probably circumvent the problem in my particular application by having B inherit from AAdapter instead of having an AAdapter object as a member, but I would still like to know how to solve the problem without having to do that.
EDIT 2:
Maybe some additional info on why AAdapter exists and what I want to achieve. I am implementing a kind of wrapper class around an existing C API that actually needs to be called in another process, RPC-style. So if the user wants to call a C function in the remote process, they will instead call a corresponding method in my wrapper class locally that handles all the RPC stuff like type conversions, the actual remote call and other ugly details. This wrapper class is represented by B in my code above. Now my wrapper method signature will usually not have the exact same signature as the C function. For example, the wrapper may have std::string_view instead of a pair of const char*, size_t that the C function has. For reasons that are not important here, it also needs to have an output parameter (a pointer) where the C function has a return value instead sometimes.
In order for me to not have to define two separate method signatures (in actuality it is three) and write code to convert the parameters for every single one, I instead pass only one of the signatures as template parameters RetT, ArgsT... to B. A signature conversion class (AAdapter in the example above) then applies rules for how to generate the second signature automatically from this first one by adding parameters, changing their types, etc.. A would then hold this generated signature, and B would have the one I provided initially. However, I want B to provide an invoke() method with the signature of A, thus hiding A and the entire method signature mess from the user completely. This is why I need access to the template parameter types of A from within B, and why I can't simply remove the middle class AAdapter.
The core of your problem is turning a tuple into an argument pack.
maybe the tuple type is not the template arguments? in this case, there is a simple solution by inheritance:
#include <vector>
#include <iostream>
#include <tuple>
template<typename... Types>
struct BImpl{
typedef std::tuple<std::vector<Types>...> tuple_type;
// maybe you will get a tuple type from some class templates. assume the 'tuple_type' is the result.
// requirement: 'tuple_type' = std::tuple<SomeTypes...>
// requirement: 'tuple_type' can be deduced definitely from template arguments 'Types...'.
template<typename> // you can add other template arguments, even another std::tuple.
struct OptCallHelper;
template<typename... Args>
struct OptCallHelper<std::tuple<Args...>>{
auto dosomething(Args&&... args) /* const? noexcept? */{
// do what you want...
// requirement: you can definitely define the 'dosomething' here without any other informations.
std::cout << "implement it here." << std::endl;
}
};
typedef OptCallHelper<tuple_type> OptCall;
};
template<typename... Types>
struct B : private BImpl<Types...>::OptCall{
typedef typename BImpl<Types...>::OptCall base;
using base::dosomething;
// obviously, you can't change the implementation here.
// in other words, the definition of 'dosomething' can only depend on template arguments 'Types...'.
};
int main(){
B<int, float> b;
b({}, {}); // shows "implement it here."
return 0;
}
you can do what you want to do in BImpl and then use B instead.
// This needs to have the same method signature (return type and argument types) as AAdapter::doSomething()
template <size_t... Index>
typename AAdapter::ReturnType doSomething (
typename std::tuple_element<Index, typename AAdapter::ArgTypes>::type... args
) {
return a.doSomething(args...);
}
for AAdaptor, I think you just want the interface of dosomething in A, and you can deduce it:
#include <iostream>
template<typename...>
struct AAdaptor{
int dosomething(){
std::cout << "???" << std::endl;
return 0;
}
};
// ignore the implementation of AAdaptor and A.
// just consider of how to get the interface of 'dosomething'.
template<typename... Types>
struct BImpl{
typedef AAdaptor<Types...> value_type;
typedef decltype(&value_type::dosomething) function_type;
// attention: it won't work if 'AAdaptor::dosomething' is function template or overloaded.
// in this case, you should let A or AAdaptor give a lot of tuples to declare 'dosomething', referring to the first solution.
template<typename>
struct OptCallHelper;
template<typename Ret, typename Klass, typename... Args>
struct OptCallHelper<Ret(Klass::*)(Args...)>{
value_type data;
Ret dosomething(Args... args){
return data.dosomething(args...);
}
};
// attention: 'Ret(Klass::*)(Args...)' is different from 'Ret(Klass::*)(Args...) const', 'noexcept' as well in C++17.
// even Ret(Klass::*)(Args..., ...) is also different from them.
// you have to specialize all of them.
typedef OptCallHelper<function_type> OptCall;
};
template<typename... Types>
struct B : BImpl<Types...>::OptCall{
typedef typename BImpl<Types...>::OptCall base;
using base::dosomething;
};
int main(){
B<int, float> b;
b(); // shows "???"
return 0;
}
if there is some difference between this code and your requirement, try to give another example to imply some of your implementation. it's still not clear what B gets and should do.
This demonstrates how you can get a function with the argument types from a tuple:
#include <iostream>
#include <tuple>
#include <utility>
template <
typename ArgTuple
>
class B_Class {};
template <typename... ArgTypes>
class B_Class<std::tuple<ArgTypes...> > {
public:
static void b(
ArgTypes...
) {
std::cout << "successful call" << std::endl;
}
};
int main() {
using ArgTypes = std::tuple<int, char, float, double>;
int i; char c; float f; double d;
B_Class<ArgTypes>::b(i, c, f, d);
}
This compiles and prints "successful call" when run.
I have a templatized class like so :
template<typename T>
class A
{
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
}
I would like to add just ONE member function that would work only for 1 given type of T. Is it possible to do that at all without having to specialize the class and reimplement all the other already existing methods?
Thanks
The simplest and cleanest solution is to use a static_assert() in the body of a method, rejecting other types than the selected one (in the below example only integers are accepted):
#include <type_traits>
#include <vector>
template <typename T>
class A
{
public:
void onlyForInts(T t)
{
static_assert(std::is_same<T, int>::value, "Works only with ints!");
}
protected:
std::vector<T> myVector;
};
int main()
{
A<int> i;
i.onlyForInts(1); // works !
A<float> f;
//f.onlyForInts(3.14f); // does not compile !
}
OK CASE DEMO
NOK CASE DEMO
This utilizes the fact that a compiler instantiates a member function of a class template only when one is actually used (not when the class template is instantiated itself). And with the above solution, when a compiler tries to do so, it fails due to the execution of a static_assert.
C++ Standard Reference:
§ 14.7.1 Implicit instantiation [temp.inst]
Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.
[ Example:
template<class T> struct Z {
void f();
void g();
};
void h() {
Z<int> a; // instantiation of class Z<int> required
Z<char>* p; // instantiation of class Z<char> not required
Z<double>* q; // instantiation of class Z<double> not required
a.f(); // instantiation of Z<int>::f() required
p->g(); // instantiation of class Z<char> required, and
// instantiation of Z<char>::g() required
}
Nothing in this example requires class Z<double>, Z<int>::g(), or Z<char>::f() to be implicitly
instantiated. — end example ]
Yes, it's possible in C++03 with CRTP (Curiously recurring template pattern):
#include <numeric>
#include <vector>
template<typename Derived, typename T>
struct Base
{
};
template<typename Derived>
struct Base<Derived, int>
{
int Sum() const
{
return std::accumulate(static_cast<Derived const*>(this)->myVector.begin(), static_cast<Derived const*>(this)->myVector.end(), int());
}
};
template<typename T>
class A : public Base<A<T>, T>
{
friend class Base<A<T>, T>;
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
};
int main()
{
A<int> Foo;
Foo.Sum();
}
As an alternative solution, which works also in plain C++03 (as opposed to static_assert or enable_if solutions), you may add extra defaulted template argument which will let you have both
specialized and unspecialized version of class. Then you can inherit your specialized version from the unspecialized one.
Here is a sample snippet:
#include <vector>
template<typename T, bool unspecialized = false>
class A
{
protected:
std::vector<T> myVector;
public:
void setVec(const std::vector<T>& vec) { myVector = vec; }
/*
constructors + a bunch of member functions here
*/
};
template<>
class A<int, false> : public A<int, true>
{
public:
int onlyForInt() {
return 25;
}
};
int main() {
// your code goes here
std::vector<int> vec;
A<int> a;
a.setVec(vec);
a.onlyForInt();
return 0;
}
The drawbacks of this solution is the need to add constructor forwarders, if class
has non-trivial constructors.
The static_assert technique by #PiotrS. works nicely. But it's also nice to know that you can specialize a single member function without code duplication. Just give the generic onlyForInts() an empty no-op implementation, and specialize it out-of-class for int
#include <vector>
template <typename T>
class A
{
public:
void onlyForInts(T t)
{
// no-op
}
protected:
std::vector<T> myVector;
};
template<>
void A<int>::onlyForInts(int t)
{
// works
}
int main()
{
A<int> i;
i.onlyForInts(1); // works !
A<float> f;
f.onlyForInts(3.14f); // compiles, but does nothing !
}
Live Example.
This technique comes in handy if you want to have int specific behavior without completely disabling the generic behavior.
One approach not given yet in the answers is using the standard library std::enable_if to perform SFINAE on a base class that you inherit to the main class that defines appropriate member functions.
Example code:
template<typename T, class Enable = void>
class A_base;
template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>{
public:
void only_for_ints(){/* integer-based function */}
};
template<typename T>
class A_base<T, typename std::enable_if<!std::is_integral<T>::value>::type>{
public:
// maybe specialize for non-int
};
template<typename T>
class A: public A_base<T>{
protected:
std::vector<T> my_vector;
};
This approach would be better than an empty function because you are being more strict about your API and better than a static_cast because it simply won't make it to the inside of the function (it won't exist) and will give you a nice error message at compile time (GCC shows "has no member named ‘only_for_ints’" on my machine).
The downside to this method would be compile time and code bloat, but I don't think it's too hefty.
(don't you dare say that C++11 requirement is a down-side, we're in 2014 god-damnit and the next standard has even be finalized already!)
Also, I noticed, you will probably have to define my_vector in the base class instead of the final because you probably want to handle that data within the member function.
A nice way to do that without duplicating a bunch of code is to create a base base class (good god) and inherit that class in the base class.
Example:
template<typename T>
class base_data{
protected:
std::vector<T> my_vector;
};
template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>: public base_bata<T>{
public:
void only_for_ints(){/* phew, finally. fiddle around with my_vector! */}
};
// non-integer A-base
template<typename T>
class A: public A_base<T>{
protected:
// helper functions not available in base
};
That does leave a horrible looking multiple-inheritance scheme, but it is very workable and makes it easy to define members based on template parameters (for future proofing).
People often don't like multiple-inheritance or how complicated/messy SFINAE looks, but I couldn't live without it now that I know of it: the speed of static code with the polymorphism of dynamic code!
Not sure where I found this, but you can use = delete; as the function definition inside the class, thereby deleting the function for the general case, and then explicitly specialize outside the class:
template <typename T>
struct A
{
auto int_only(T) -> void = delete;
};
template <> auto A<int>::int_only(int) -> void {}
int main()
{
auto a_int = A<int>{};
auto a_dbl = A<double>{};
a_int.int_only(0);
// a_dbl.int_only(3.14); error: call to deleted member function
}
https://en.cppreference.com/w/cpp/language/function#Deleted_functions
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.
I have a templated class Converter, and I'd like to do a partial specialization. The tricky part is I'd like to specialize it to MyFoo::Vec where MyFoo again can be specialized as a template parameter. If that sounds confusing, maybe the code itself makes it clearer:
#include <iostream>
#include <vector>
template<class To>
struct Converter {
Converter(int from, To& to) {
to = To(from);
}
};
template<class T>
struct Foo {
typedef std::vector<T> Vec;
Vec vec;
};
// Template specialization: Convert from 'From' to 'MyFoo::Vec':
template<class MyFoo>
struct Converter<typename MyFoo::Vec > { // Error: template parameters not
// used in partial specialization
Converter(int from, typename MyFoo::Vec& to) {
to.push_back(typename MyFoo::Vec::value_type(from));
}
};
int main() {
Foo<float> myfoo;
Converter<Foo<float> > converter(2, myfoo.vec);
}
This is just a mini example derived from my actual code. This question is not about how useful such a converter is; I'm just interested in getting the syntax right given that I need such a converter and its specialization.
It cannot be done directly. Consider that it is impossible to go from the nested type to the enclosing type for two reasons: first, the mapping might not be unique (multiple Foo might have the same nested Vec type) and even if it was the compiler would have to test all existing types (i.e. it cannot infer from the instantiation).
What you want to do can actually be done with SFINAE (untested code, you can read more here):
template <typename T, typename V = void>
struct Converter {}; // Default implementation
template <typename T>
struct Converter<T, T::Vec> {}; // specific if has nested Vec
Assume I have a template (called ExampleTemplate) that takes two arguments: a container type (e.g. list, vector) and a contained type (e.g. float, bool, etc). Since containers are in fact templates, this template has a template param. This is what I had to write:
#include <vector>
#include <list>
using namespace std;
template < template <class,class> class C, typename T>
class ExampleTemplate {
C<T,allocator<T> > items;
public:
....
};
main()
{
ExampleTemplate<list,int> a;
ExampleTemplate<vector,float> b;
}
You may ask what is the "allocator" thing about. Well, Initially, I tried the obvious thing...
template < template <class> class C, typename T>
class ExampleTemplate {
C<T> items;
};
...but I unfortunately found out that the default argument of the allocator...
vector<T, Alloc>
list<T, Alloc>
etc
...had to be explicitely "reserved" in the template declaration.
This, as you can see, makes the code uglier, and forces me to reproduce the default values of the template arguments (in this case, the allocator).
Which is BAD.
EDIT: The question is not about the specific problem of containers - it is about "Default values in templates with template arguments", and the above is just an example. Answers depending on the knowledge that STL containers have a "::value_type" are not what I am after. Think of the generic problem: if I need to use a template argument C in a template ExampleTemplate, then in the body of ExampleTemplate, do I have to reproduce the default arguments of C when I use it? If I have to, doesn't that introduce unnecessary repetition and other problems (in this case, where C is an STL container, portability issues - e.g. "allocator" )?
Perhaps you'd prefer this:
#include <vector>
#include <list>
using namespace std;
template <class Container>
class ForExamplePurposes {
typedef typename Container::value_type T;
Container items;
public:
};
int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
}
This uses "static duck typing". It is also a bit more flexible as it doesn't force the Container type to support STL's Allocator concept.
Perhaps using the type traits idiom can give you a way out:
#include <vector>
#include <list>
using namespace std;
struct MyFunkyContainer
{
typedef int funky_type;
// ... rest of custom container declaration
};
// General case assumes STL-compatible container
template <class Container>
struct ValueTypeOf
{
typedef typename Container::value_type type;
};
// Specialization for MyFunkyContainer
template <>
struct ValueTypeOf<MyFunkyContainer>
{
typedef MyFunkyContainer::funky_type type;
};
template <class Container>
class ForExamplePurposes {
typedef typename ValueTypeOf<Container>::type T;
Container items;
public:
};
int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
ForExamplePurposes< MyFunkyContainer > c;
}
Someone who wants to use ForExamplePurposes with a non-STL-compliant container would need to specialize the ValueTypeOf traits class.
I would propose to create adapters.
Your class should be created with the exact level of personalization that is required by the class:
template <template <class> C, template T>
class Example
{
typedef T Type;
typedef C<T> Container;
};
EDIT: attempting to provide more is nice, but doomed to fail, look at the various expansions:
std::vector<T>: std::vector<T, std::allocator<T>>
std::stack<T>: std::stack<T, std::deque<T>>
std::set<T>: std::set<T, std::less<T>, std::allocator<T>>
The second is an adapter, and so does not take an allocator, and the third does not have the same arity. You need therefore to put the onus on the user.
If a user wishes to use it with a type that does not respect the expressed arity, then the simplest way for him is to provide (locally) an adapter:
template <typename T>
using Vector = std::vector<T>; // C++0x
Example<Vector, bool> example;
I am wondering about the use of parameter packs (variadic templates) here... I don't know if declaring C as template <class...> C would do the trick or if the compiler would require a variadic class then.
You have to give the full template signature, including default parameters, if you want to be able to use the template template parameter the usual way.
template <typename T, template <class U, class V = allocator<U> > class C>
class ExampleTemplate {
C<T> items;
public:
....
};
If you want to handle other containers that the one from the STL, you can delegate container construction to a helper.
// Other specialization failed. Instantiate a std::vector.
template <typename T, typename C>
struct make_container_
{
typedef std::vector<T> result;
};
// STL containers
template <typename T, template <class U, class V = allocator<U> > class C>
struct make_container_<T,C>
{
typedef C<T> result;
};
// Other specializations
...
template <typename T, typename C>
class ExampleTemplate {
make_container_<T,C>::result items;
public:
....
};
I think, it is required to reproduce all template parameters, even default. Note, that Standard itself does not use template template parameters for containter adaptors, and prefers to use regular template parameters:
template < class T , class Container = deque <T > > class queue { ... };
template < class T , class Container = vector <T>, class Compare = less < typename Container :: value_type > > class priority_queue { ... };
The following code will allow you to do something like you're asking for. Of course, this won't work with standard containers, since this has to already be part of the template class that's being passed into the template.
/* Allows you to create template classes that allow users to specify only some
* of the default parameters, and some not.
*
* Example:
* template <typename A = use_default, typename B = use_default>
* class foo
* {
* typedef use_default_param<A, int> a_type;
* typedef use_default_param<B, double> b_type;
* ...
* };
*
* foo<use_default, bool> x;
* foo<char, use_default> y;
*/
struct use_default;
template<class param, class default_type>
struct default_param
{
typedef param type;
};
template<class default_type>
struct default_param<use_default, default_type>
{
typedef default_type type;
};
But I don't really think this is what you're looking for. What you're doing with the containers is unlikely to be applicable to arbitrary containers as many of them will have the problem you're having with multiple default parameters with non-obvious types as defaults.
As the question exactly described the problem I had in my code (--I'm using Visual Studio 2015), I figured out an alternative solution which I wanted to share.
The idea is the following: instead of passing a template template parameter to the ExampleTemplate class template, one can also pass a normal typename which contains a type DummyType as dummy parameter, say std::vector<DummyType>.
Then, inside the class, one replace this dummy parameter by something reasonable. For replacement of the typethe following helper classes can be used:
// this is simply the replacement for a normal type:
// it takes a type T, and possibly replaces it with ReplaceByType
template<typename T, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type
{
using type = std::conditional_t<std::is_same<T, ReplaceWhatType>::value, ReplaceByType, T>;
};
// this sets up the recursion, such that replacement also happens
// in contained nested types
// example: in "std::vector<T, allocator<T> >", both T's are replaced
template<template<typename ...> class C, typename ... Args, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type<C<Args ...>, ReplaceWhatType, ReplaceByType>
{
using type = C<typename replace_type<Args, ReplaceWhatType, ReplaceByType>::type ...>;
};
// an alias for convenience
template<typename ... Args>
using replace_type_t = typename replace_type<Args ...>::type;
Note the recursive step in replace_type, which takes care that types nested in other classes are replaced as well -- with this, for example, in std::vector<T, allocator<T> >, both T's are replaced and not only the first one. The same goes for more than one nesting hierarchy.
Next, you can use this in your ExampleTemplate-class,
struct DummyType {};
template <typename C, typename T>
struct ExampleTemplate
{
replace_type_t<C, DummyType, T> items;
};
and call it via
int main()
{
ExampleTemplate<std::vector<DummyType>, float> a;
a.items.push_back(1.0);
//a.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::list<DummyType>, float> b;
b.items.push_back(1.0);
//b.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::map<int, DummyType>, float> c;
c.items[0]=1.0;
//c.items[0]="Hello"; // prints an error message which shows that DummyType is replaced correctly
}
DEMO
Beside the not-that-nice syntac, this has the advantage that
It works with any number of default template parameters -- for instance, consider the case with std::map in the example.
There is no need to explicitly specify any default template parameters whatsoever.
It can be easily extended to more dummy parameters (whereas then it probably should not be called by users ...).
By the way: Instead of the dummy type you can also use the std::placeholder's ... just realized that it might be a bit nicer.