I tried this:
template<typename P, typename = std::enable_if_t<std::is_arithmetic<P>::value>>
void f(std::vector<P>* a) {
// body for arithmetic P
}
template<typename P, typename = std::enable_if_t<std::is_class<P>::value>>
void f(std::vector<P>* a) {
// body for class P
}
I thought it would overload f as the conditions are mutually exclusive, but found that it doesn't compile: "function template has already been defined".
What to do instead, if I want the function body of f(std::vector<P>*) to depend on whether P is arithmetic?
The std::enable_if documentation on cppreference.com says:
A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.
The examples on that same page show a similar situation as yours, and solve it by changing the template on one of the overloads, while maintaining the same signature for the functions themselves:
// #4, enabled via a template parameter
template<class T,
typename std::enable_if<
!std::is_trivially_destructible<T>{} &&
(std::is_class<T>{} || std::is_union<T>{}),
int>::type = 0>
void destroy(T* t)
{
std::cout << "destroying non-trivially destructible T\n";
t->~T();
}
// #5, enabled via a template parameter
template<class T,
typename = std::enable_if_t<std::is_array<T>::value> >
void destroy(T* t) // note, function signature is unmodified
{
for(std::size_t i = 0; i < std::extent<T>::value; ++i) {
destroy((*t)[i]);
}
}
/*
template<class T,
typename = std::enable_if_t<std::is_void<T>::value> >
void destroy(T* t){} // error: has the same signature with #5
*/
So, you can do something similar in your code:
template<typename P, std::enable_if_t<std::is_arithmetic<P>::value, int> = 0>
void f(std::vector<P>* a)
{
// body for arithmetic P
}
template<typename P, typename = std::enable_if_t<std::is_class<P>::value>>
void f(std::vector<P>* a)
{
// body for class P
}
Live Demo
Use tag dispatch, like this or similar:
void f_helper(std::vector<P>* a, std::true_type) {
/* implementation for arithmetic type P */
}
void f_helper(std::vector<P>* a, std::false_type) {
/* implementation for class type P */
}
void f(std::vector<P>* a) {
return f_helper(a, std::is_arithmetic<P>{});
}
Related
For example, given the following code
class A {
public:
double operator()(double foo) {
return foo;
}
};
class B {
public:
double operator()(double foo, int bar) {
return foo + bar;
}
};
I want to write two versions of fun, one that works with objects with A's signature and another one that works with objects with B's signature:
template <typename F, typename T>
T fun(F f, T t) {
return f(t);
}
template <typename F, typename T>
T fun(F f, T t) {
return f(t, 2);
}
And I expect this behavior
A a();
B b();
fun(a, 4.0); // I want this to be 4.0
fun(b, 4.0); // I want this to be 6.0
Of course the previous example throws a template redefinition error at compile time.
If B is a function instead, I can rewrite fun to be something like this:
template <typename T>
T fun(T (f)(T, int), T t) {
return f(t, 2);
}
But I want fun to work with both, functions and callable objects. Using std::bind or std::function maybe would solve the problem, but I'm using C++98 and those were introduced in C++11.
Here's a solution modified from this question to accommodate void-returning functions. The solution is simply to use sizeof(possibly-void-expression, 1).
#include <cstdlib>
#include <iostream>
// like std::declval in c++11
template <typename T>
T& decl_val();
// just use the type and ignore the value.
template <std::size_t, typename T = void>
struct ignore_value {typedef T type;};
// This is basic expression-based SFINAE.
// If the expression inside sizeof() is invalid, substitution fails.
// The expression, when valid, is always of type int,
// thanks to the comma operator.
// The expression is valid if an F is callable with specified parameters.
template <class F>
typename ignore_value<sizeof(decl_val<F>()(1),1), void>::type
call(F f)
{
f(1);
}
// Same, with different parameters passed to an F.
template <class F>
typename ignore_value<sizeof(decl_val<F>()(1,1),1), void>::type
call(F f)
{
f(1, 2);
}
void func1(int) { std::cout << "func1\n"; }
void func2(int,int) { std::cout << "func2\n"; }
struct A
{
void operator()(int){ std::cout << "A\n"; }
};
struct B
{
void operator()(int, int){ std::cout << "B\n"; }
};
struct C
{
void operator()(int){ std::cout << "C1\n"; }
void operator()(int, int){ std::cout << "C2\n"; }
};
int main()
{
call(func1);
call(func2);
call(A());
call(B());
// call(C()); // ambiguous
}
Checked with gcc and clang in c++98 mode.
I am trying to implement a resource protection class which would combine data along with a shared mutex (actually, QReadWriteLock, but it's similar). The class must provide the method to apply a user-defined function to the data when the lock is acquired. I would like this apply method to work differently depending on the function parameter (reference, const reference, or value). For example, when the user passes a function like int (const DataType &) it shouldn't block exclusively as we are just reading the data and, conversely, when the function has the signature like void (DataType &) that implies data modification, hence the exclusive lock is needed.
My first attempt was to use std::function:
template <typename T>
class Resource1
{
public:
template <typename Result>
Result apply(std::function<Result(T &)> &&f)
{
QWriteLocker locker(&this->lock); // acquire exclusive lock
return std::forward<std::function<Result(T &)>>(f)(this->data);
}
template <typename Result>
Result apply(std::function<Result(const T &)> &&f) const
{
QReadLocker locker(&this->lock); // acquire shared lock
return std::forward<std::function<Result (const T &)>>(f)(this->data);
}
private:
T data;
mutable QReadWriteLock lock;
};
But std::function doesn't seem to restrict parameter constness, so std::function<void (int &)> can easily accept void (const int &), which is not what I want. Also in this case it can't deduce lambda's result type, so I have to specify it manually:
Resource1<QList<int>> resource1;
resource1.apply<void>([](QList<int> &lst) { lst.append(11); }); // calls non-const version (ok)
resource1.apply<int>([](const QList<int> &lst) -> int { return lst.size(); }); // also calls non-const version (wrong)
My second attempt was to use std::result_of and return type SFINAE:
template <typename T>
class Resource2
{
public:
template <typename F>
typename std::result_of<F (T &)>::type apply(F &&f)
{
QWriteLocker locker(&this->lock); // lock exclusively
return std::forward<F>(f)(this->data);
}
template <typename F>
typename std::result_of<F (const T &)>::type apply(F &&f) const
{
QReadLocker locker(&this->lock); // lock non-exclusively
return std::forward<F>(f)(this->data);
}
private:
T data;
mutable QReadWriteLock lock;
};
Resource2<QList<int>> resource2;
resource2.apply([](QList<int> &lst) {lst.append(12); }); // calls non-const version (ok)
resource2.apply([](const QList<int> &lst) { return lst.size(); }); // also calls non-const version (wrong)
Mainly the same thing happens: as long as the object is non-const the mutable version of apply gets called and result_of doesn't restrict anything.
Is there any way to achieve this?
You may do the following
template <std::size_t N>
struct overload_priority : overload_priority<N - 1> {};
template <> struct overload_priority<0> {};
using low_priority = overload_priority<0>;
using high_priority = overload_priority<1>;
template <typename T>
class Resource
{
public:
template <typename F>
auto apply(F&& f) const
// -> decltype(apply_impl(std::forward<F>(f), high_priority{}))
{
return apply_impl(std::forward<F>(f), high_priority{});
}
template <typename F>
auto apply(F&& f)
// -> decltype(apply_impl(std::forward<F>(f), high_priority{}))
{
return apply_impl(std::forward<F>(f), high_priority{});
}
private:
template <typename F>
auto apply_impl(F&& f, low_priority) -> decltype(f(std::declval<T&>()))
{
std::cout << "ReadLock\n";
return std::forward<F>(f)(this->data);
}
template <typename F>
auto apply_impl(F&& f, high_priority) -> decltype(f(std::declval<const T&>())) const
{
std::cout << "WriteLock\n";
return std::forward<F>(f)(this->data);
}
private:
T data;
};
Demo
Jarod has given a workaround, but I'll explain why you cannot achieve that this regular way.
The problem is that:
Overload resolution prefers non-const member functions over const member functions when called from a non-const object
whatever object this signature void foo(A&) can accept, void foo(const A&) can also the same object. The latter even has a broader binding set than the former.
Hence, to solve it, you will have to at least defeat point 1 before getting to 2. As Jarod has done.
From your signatures (see my comment annotations):
template <typename F>
typename std::result_of<F (T &)>::type apply(F &&f) //non-const member function
{
return std::forward<F>(f)(this->data);
}
template <typename F>
typename std::result_of<F (const T &)>::type apply(F &&f) const //const member function
{
return std::forward<F>(f)(this->data);
}
When you call it like:
resource2.apply([](QList<int> &lst) {lst.append(12); }); //1
resource2.apply([](const QList<int> &lst) { return lst.size(); }); //2
First of all, remember that resource2 isn't a const reference. Hence, the non-const membr function of apply will always be prefered by Overload resolution.
Now, taking the case of the first call //1, Whatever that lambda is callable with, then then the second one is also callable with that object
A simplified mock-up of what you are trying to do is:
struct A{
template<typename Func>
void foo(Func&& f); //enable if we can call f(B&);
template<typename Func>
void foo(Func&& f) const; //enable if we can call f(const B&);
};
void bar1(B&);
void bar2(const B&);
int main(){
A a;
a.foo(bar1);
a.foo(bar2);
//bar1 and bar2 can be both called with lvalues
B b;
bar1(b);
bar2(b);
}
As I understand it, you want to discriminate a parameter that's a std::function that takes a const reference versus a non-constant reference.
The following SFINAE-based approach seems to work, using a helper specialization class:
#include <functional>
#include <iostream>
template<typename ...Args>
using void_t=void;
template<typename Result,
typename T,
typename lambda,
typename void_t=void> class apply_helper;
template <typename T>
class Resource1
{
public:
template <typename Result, typename lambda>
Result apply(lambda &&l)
{
return apply_helper<Result, T, lambda>::helper(std::forward<lambda>(l));
}
};
template<typename Result, typename T, typename lambda, typename void_t>
class apply_helper {
public:
static Result helper(lambda &&l)
{
std::cout << "T &" << std::endl;
T t;
return l(t);
}
};
template<typename Result, typename T, typename lambda>
class apply_helper<Result, T, lambda,
void_t<decltype( std::declval<lambda>()( std::declval<T>()))>> {
public:
static Result helper(lambda &&l)
{
std::cout << "const T &" << std::endl;
return l( T());
}
};
Resource1<int> test;
int main()
{
auto lambda1=std::function<char (const int &)>([](const int &i)
{
return (char)i;
});
auto lambda2=std::function<char (int &)>([](int &i)
{
return (char)i;
});
auto lambda3=[](const int &i) { return (char)i; };
auto lambda4=[](int &i) { return (char)i; };
test.apply<char>(lambda1);
test.apply<char>(lambda2);
test.apply<char>(lambda3);
test.apply<char>(lambda4);
}
Output:
const T &
T &
const T &
T &
Demo
The helper() static class in the specialized class can now be modified to take a this parameter, instead, and then use it to trampoline back into the original template's class's method.
As long as the capture lists of your lambdas are empty, you can rely on the fact that such a lambda decays to a function pointer.
It's suffice to discriminate between the two types.
It follows a minimal, working example:
#include<iostream>
template <typename T>
class Resource {
public:
template <typename Result>
Result apply(Result(*f)(T &)) {
std::cout << "non-const" << std::endl;
return f(this->data);
}
template <typename Result>
Result apply(Result(*f)(const T &)) const {
std::cout << "const" << std::endl;
return f(this->data);
}
private:
T data;
};
int main() {
Resource<int> resource;
resource.apply<void>([](int &lst) { });
resource.apply<int>([](const int &lst) -> int { return 42; });
}
Problem statement (for an educational purpose):
-Implement method printContainer which works for STL containers vector, stack, queue and deque.
I made a solution, but I don`t like it due to excessive amount of code.
What I did to solve the problem:
1. Designed generic function which expects uniform interface from containers for operations: get value of last element and erase that element from the container
template <typename T>
void printContainer(T container)
{
cout << " * * * * * * * * * * " << endl;
cout << " operator printContainer(T container). Stack, queue, priority queue"
<< endl;
cout << typeid(container).name() << endl;
while (!container.empty())
{
cout << top(container) << " ";
pop(container);
}
cout << endl;
cout << " * * * * * * * * * * * " << endl;
}
For each container I implemented functions that allows to provide uniform interface
(I want to refactor the following code snippet):
template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
return v.back();
}
template <typename T, typename Base>
typename stack<T, Base>::value_type top(const stack<T, Base>& s)
{
return s.top();
}
template <typename T, typename Base>
typename queue<T, Base>::value_type top(const queue<T, Base>& q)
{
return q.front();
}
template <typename T, typename Base>
typename priority_queue<T, Base>::value_type top(const priority_queue<T,
Base>& pq)
{
return pq.top();
}
template <typename T>
void pop(vector<T>& v)
{
return v.pop_back();
}
template <typename T, typename Base>
void pop(stack<T, Base>& s)
{
return s.pop();
}
template <typename T, typename Base>
void pop(queue<T, Base>& q)
{
return q.pop();
}
template <typename T, typename Base>
void pop(priority_queue<T,Base>& pq)
{
return pq.pop();
}
I wan`t to replace it with something like this:
template <typename T, typename Base, template<typename T, class Base,
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type top(container<T,Base>& c)
{
if (typeid(container).name == typeid(vector<T,Base>))
return c.back();
if (typeid(container).name == typeid(queue<T,Base>))
return c.front();
else
return c.top();
}
template <typename T, typename Base, template<typename T, class Base,
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type pop(container<T,Base>& c)
{
if (typeid(container).name == typeid(vector<T,Base>))
c.pop_back();
else
return c.pop();
}
but it doesn`t work, I get errors like :
Error 1 error C2784: 'container<T,Base>::value_type top(container<T,Base> &)' : could not deduce template argument for 'container<T,Base> &' from 'std::stack<_Ty>'
Question:
that adjacements should I made in template template paramter to sort out errors, maybe there is something that I overlooked or exist logical errors.
Any way, any usefull information is welcomed.
Thanks in advance!
UPDATE:
// that is how I am trying to invoke the function
int arr[] = {1,2,3,4,5,6,7,8,9,0};
stack<int> s(deque<int>(arr, arr + sizeof(arr) / sizeof(arr[0])));;
queue<int> q(deque<int>(arr, arr + sizeof(arr) / sizeof(arr[0])));
priority_queue<int> pq(arr, arr + sizeof(arr) / sizeof(arr[0]));
printContainer(s);
printContainer(q);
printContainer(pq);
This solution:
template <typename T, typename Base, template<typename T, class Base,
class ALL = std::allocator<T>> class container>
typename container<T,Base>::value_type top(container<T,Base>& c)
{
if (typeid(container).name == typeid(vector<T,Base>))
return c.back();
if (typeid(container).name == typeid(queue<T,Base>))
return c.front();
else
return c.top();
}
Won't work, because if() realizes a run-time selection, which means that the code of all branches must compile, even though exactly only one of them evaluates to true, and function top() is not provided by all containers (e.g. vector).
Consider this simpler example for an explanation:
struct X { void foo() { } };
struct Y { void bar() { } };
template<bool b, typename T>
void f(T t)
{
if (b)
{
t.foo();
}
else
{
t.bar();
}
}
int main()
{
X x;
f<true>(x); // ERROR! bar() is not a member function of X
Y y;
f<false>(y); // ERROR! foo() is not a member function of Y
}
Here, I am passing a boolean template argument, which is known at compile-time, to function f(). I am passing true if the input is of type X, and therefore supports a member function called foo(); and I am passing false if the input is of type Y, and therefore supports a member function called bar().
Even though the selection works on a boolean value which is known at compile-time, the statement itself is executed at run-time. The compiler will first have to compile the whole function, including the false branch of the if statement.
What you are looking for is some kind of static if construct, which is unfortunately not available in C++.
The traditional solution here is based on overloading, and looks in fact like the one you provided originally.
I'd come at it the other way around. I'd write a generic function that uses iterators:
template <class Iter>
void show_contents(Iter first, Iter last) {
// whatever
}
then a generic function that takes containers:
template <class Container>
void show_container(const Container& c) {
show_contents(c.begin(), c.end());
}
then a hack to get at the container that underlies a queue or a stack:
template <class C>
struct hack : public C {
hack(const C& cc) : C(cc) { }
typename C::Container::const_iterator begin() const {
return this->c.begin();
}
typename C::Container::const_iterator end() const {
return this->c.end();
}
};
then define specializations to create these objects and show their contents:
template <class T>
void show_container(const stack<T>& s) {
hack<stack<T>> hack(s);
show_contents(hack.begin(), hack.end());
}
template <class T>
void show_container(const queue<T>& q) {
hack<stack<T>> hack(q);
show_contents(hack.begin(), hack.end());
}
While Andy's answer is already a good one, I'd like to add one improvement about your implementation. You could improve it to support more container specializations, as your overloads don't allow all the template parameters that the STL containers have to be non-default. For example, look at your code:
template <typename T>
typename vector<T>::value_type top(const vector<T>& v)
{
return v.back();
}
and now compare it with the definition of std::vector. The class template has two parameters, namely std::vector<T,Allocator=std::allocator<T>>. You overload only accepts those std::vectors where the second parameter is std::allocator<T>.
While you could manually add more parameters to your code, there is a better alternative: Variadic templates. You can use the following code for a truly generic version for all std::vectors:
template <typename... Ts>
typename vector<Ts...>::value_type top(const vector<Ts...>& v)
{
return v.back();
}
and, of course, you can use the same technique for all other containers and don't need to worry about the exact number of template parameters they have. Some containers even have up to five template parameters, so this can be quite annoying if you don't use variadic templates.
One caveat: Some older compilers might not like the variadic version, you'll have to manually iterate all parameters.
EDITED: Let us suposse I have two (or more) template functions f and g that uses (some times) types depending on its template parameter:
template<typename T>
some_ugly_and_large_or_deep_template_struct_1<T>::type
f(const some_ugly_and_large_or_deep_template_struct_1<T>::type&,
const some_ugly_and_large_or_deeptemplate_struct_1<T>::type&)
{
// body, that uses perhaps more times my
// "some_ugly_and_large_or_deep_template_struct_1<T>"
}
template<typename T>
some_ugly_and_large_or_deep_template_struct_2<T>::type
g(const some_ugly_and_large_or_deep_template_struct_2<T>::type&,
const some_ugly_and_large_or_deeptemplate_struct_2<T>::type&)
{
// body, that uses perhaps more times my
// "some_ugly_and_large_or_deep_template_struct_2<T>"
}
How could I simplify this "type" definition?, for example with any of new C++11's tools? I think only on something like:
template<typename T,
typename aux = some_ugly_and_large_or_deep_template_struct_1<T>::type>
aux f(const aux&, const aux&)
{
// body, that uses perhaps more times my
// "aux" type
}
template<typename T,
typename aux = some_ugly_and_large_or_deep_template_struct_2<T>::type>
aux g(const aux&, const aux&)
{
// body, that uses perhaps more times my
// "aux" type
}
The problem that I see with this approach is the user can provide his own aux type and not the type that I want.
If you make it a variadic template, the caller has no possibility to define the type parameters listed after:
template<typename T,
typename..., // firewall, absorbs all supplied arguments
typename aux = some_ugly_and_large_or_deep_template_struct_1<T>::type>
aux f(const aux&, const aux&)
{
// body, that uses perhaps more times my
// "aux" type
}
Optionally, to prevent calling f accidentally with too many template arguments, one can add a static_assert:
template<typename T,
typename... F,
typename aux = some_ugly_and_large_or_deep_template_struct_1<T>::type>
aux f(const aux&, const aux&)
{
static_assert(sizeof...(F)==0, "Too many template arguments");
// body, that uses perhaps more times my
// "aux" type
}
Usually, I can live with letting the user define types like aux, being for example the return type where this can save you a cast.
Or you can replace the static_assert with an enable_if:
template<typename T,
typename... F, typename = typename std::enable_if<sizeof...(F)==0>::type,
typename aux = some_ugly_and_large_or_deep_template_struct<T>::type,>
aux f(const aux&, const aux&)
{
// body, that uses perhaps more times my
// "aux" type
}
You could declare a template alias alongside the function:
template<typename T> using f_parameter
= typename some_ugly_and_large_or_deep_template_struct<T>::type;
template<typename T>
f_parameter<T> f(const f_parameter<T>&, const f_parameter<T>&)
{
f_parameter<T> param;
}
You can use something like
namespace f_aux {
template <typename T> using type =
typename some_ugly_and_large_or_deep_template_struct<T>::type;
}
template <typename T>
f_aux::type<T> f(const f_aux::type<T>& , const f_aux::type<T>&);
If the declaration of f is in a suitable namespace or class, you may not need the additional f_aux namespace.
A possible solution would be to convert the template function into a template struct with an operator(). For example:
#include <iostream>
#include <string>
template <typename T>
struct some_ugly_and_large_or_deep_template_struct
{
typedef T type;
};
template <typename T>
struct f
{
typedef typename some_ugly_and_large_or_deep_template_struct<T>::type aux;
aux operator()(const aux& a1, const aux& a2)
{
return a1 + a2;
}
};
int main()
{
std::cout << f<int>()(4, 4) << "\n";
std::cout << f<std::string>()("hello ", "world") << "\n";
return 0;
}
Why are default template arguments only allowed on class templates? Why can't we define a default type in a member function template? For example:
struct mycclass {
template<class T=int>
void mymember(T* vec) {
// ...
}
};
Instead, C++ forces that default template arguments are only allowed on a class template.
It makes sense to give default template arguments. For example you could create a sort function:
template<typename Iterator,
typename Comp = std::less<
typename std::iterator_traits<Iterator>::value_type> >
void sort(Iterator beg, Iterator end, Comp c = Comp()) {
...
}
C++0x introduces them to C++. See this defect report by Bjarne Stroustrup: Default Template Arguments for Function Templates and what he says
The prohibition of default template arguments for function templates is a misbegotten remnant of the time where freestanding functions were treated as second class citizens and required all template arguments to be deduced from the function arguments rather than specified.
The restriction seriously cramps programming style by unnecessarily making freestanding functions different from member functions, thus making it harder to write STL-style code.
To quote C++ Templates: The Complete Guide (page 207):
When templates were originally added to the C++ language, explicit function template arguments were not a valid construct. Function template arguments always had to be deducible from the call expression. As a result, there seemed to be no compelling reason to allow default function template arguments because the default would always be overridden by the deduced value.
So far, all the proffered examples of default template parameters for function templates can be done with overloads.
AraK:
struct S {
template <class R = int> R get_me_R() { return R(); }
};
could be:
struct S {
template <class R> R get_me_R() { return R(); }
int get_me_R() { return int(); }
};
My own:
template <int N = 1> int &increment(int &i) { i += N; return i; }
could be:
template <int N> int &increment(int &i) { i += N; return i; }
int &increment(int &i) { return increment<1>(i); }
litb:
template<typename Iterator, typename Comp = std::less<Iterator> >
void sort(Iterator beg, Iterator end, Comp c = Comp())
could be:
template<typename Iterator>
void sort(Iterator beg, Iterator end, std::less<Iterator> c = std::less<Iterator>())
template<typename Iterator, typename Comp >
void sort(Iterator beg, Iterator end, Comp c = Comp())
Stroustrup:
template <class T, class U = double>
void f(T t = 0, U u = 0);
Could be:
template <typename S, typename T> void f(S s = 0, T t = 0);
template <typename S> void f(S s = 0, double t = 0);
Which I proved with the following code:
#include <iostream>
#include <string>
#include <sstream>
#include <ctype.h>
template <typename T> T prettify(T t) { return t; }
std::string prettify(char c) {
std::stringstream ss;
if (isprint((unsigned char)c)) {
ss << "'" << c << "'";
} else {
ss << (int)c;
}
return ss.str();
}
template <typename S, typename T> void g(S s, T t){
std::cout << "f<" << typeid(S).name() << "," << typeid(T).name()
<< ">(" << s << "," << prettify(t) << ")\n";
}
template <typename S, typename T> void f(S s = 0, T t = 0){
g<S,T>(s,t);
}
template <typename S> void f(S s = 0, double t = 0) {
g<S,double>(s, t);
}
int main() {
f(1, 'c'); // f<int,char>(1,'c')
f(1); // f<int,double>(1,0)
// f(); // error: T cannot be deduced
f<int>(); // f<int,double>(0,0)
f<int,char>(); // f<int,char>(0,0)
}
The printed output matches the comments for each call to f, and the commented-out call fails to compile as expected.
So I suspect that default template parameters "aren't needed", but probably only in the same sense that default function arguments "aren't needed". As Stroustrup's defect report indicates, the addition of non-deduced parameters was too late for anyone to realise and/or really appreciate that it made defaults useful. So the current situation is in effect based on a version of function templates which was never standard.
On Windows, with all versions of Visual Studio you can convert this error (C4519) to a warning or disable it like so:
#ifdef _MSC_VER
#pragma warning(1 : 4519) // convert error C4519 to warning
// #pragma warning(disable : 4519) // disable error C4519
#endif
See more details here.
What I use is next trick:
Lets say you want to have function like this:
template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
E one(1);
array.add( one );
}
You will not be allowed, but I do next way:
template <typename T>
struct MyArray_t {
void add(T i)
{
// ...
}
};
template <typename E, typename ARR_E = MyArray_t<E> >
class worker {
public:
/*static - as you wish */ ARR_E* parr_;
void doStuff(); /* do not make this one static also, MSVC complains */
};
template <typename E, typename ARR_E>
void worker<E, ARR_E>::doStuff()
{
E one(1);
parr_->add( one );
}
So this way you may use it like this:
MyArray_t<int> my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();
As we can see no need to explicitly set second parameter.
Maybe it will be useful for someone.