Overload resolution with universal references - c++

I have a function which can accept any type by universal reference, and would like to overload it for specific types (some of which are templated themselves, although I don't think that's important here). Unfortunately I can't quite seem to to get the overloads to be resolved in the right order.
I would have presumed that the second declaration of foo would be preferred as it's more specific (less templated), although it looks like my understanding of overload resolution is lacking somewhat. Interestingly changing the second declaration to take X by value makes it print "good, good" and making it take X by non-const reference makes it print "bad, good". Obviously removing the first declaration entirely makes it return "good, good", as there's no other choice.
So why does this happen? And most importantly, if the following code doesn't work, how can you overload a function with this signature?
#include <iostream>
#include <string>
class X {};
template<typename T>
inline std::string foo(T && rhs) {
return "bad";
}
inline std::string foo(const X & rhs) {
return "good";
}
int main() {
std::cout << foo(X()) << std::endl;
X x;
std::cout << foo(x) << std::endl;
return 0;
}
Edit:
Maybe a more roundabout solution to this is to do it indirectly. Get rid of the first form of foo and use SFINAE to check if a valid overload exists, it it doesn't then call foo_fallback.

To answer your question.comment to Kerre's answer, you could try to use SFINAE:
#include <type_traits>
#include <string>
template <class T>
struct HasFooImpl_ {
template <typename C>
static std::true_type test(decltype(fooImpl(std::declval<C>()))*);
template <typename C>
static std::false_type test(...);
typedef decltype(test<T>(0)) type;
};
template <typename T>
using HasFooImpl = typename HasFooImpl_<T>::type;
template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return fooImpl(std::forward<T>(t));
}
template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return "generic!";
}
You'd have to implement a function fooImpl for any type that you don't want to be handled genericly.
The implementation was a bit tricky, I tried just enable_if<is_same<string, decltype(fooImpl(declval<C>()))>::value first, but for the fallback the !is_same<>::value gave me compiler errors, because it tried to instantiate the decltype as well.
This implementation has one caveat that you might or might not want to use: if T is convertible to some other type that has a fooImpl defined, that conversion will kick in.
You can see the whole thing in action here: http://ideone.com/3Tjtvj
Update:
if you don't want to allow type conversions, it actually gets easier:
#include <type_traits>
#include <string>
template <typename T> void fooImpl(T);
template <typename T>
using HasFooImpl = typename std::is_same<std::string, decltype(fooImpl(std::declval<T>()))>;
template <typename T>
typename std::enable_if<HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return fooImpl(std::forward<T>(t));
}
template <typename T>
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type
foo(T&& t)
{
return "generic!";
}
See http://ideone.com/miaoop

The conversion from X to const X is considered worse than the direct match of the templated overload with T = X or T = X &.

Related

Template Aliases - question (tour of c++)

I was going through the template Aliases from Tour of C++. I couldn't understand the below code and how to use it?
template<typename T>
class Vector {
public:
using value_type = T;
}
Here he is using the value_type as type alias for typename 'T', Why can't we just use the typename T, since we can pass any type to template(i.e. class Vector). What is the need to have alias for template?
template<typename C>
using Value_type = typename C::value_type;
Here how is value_type in the scope of C, i.e. how can we reference value_type with type 'C', since it is inside class "vector"?
does 'value_type' here mean its a Vector?
and 'Value_type' mean int::Vector or string::Vector etc..?
template<typename Container>
void algo(Container &c)
{
Vector<Value_type<Container>> vec;
}
How are these three pieces linked?
Why a member alias?
Consider you use an instantiation of Vector in some other generic code:
template <typename U>
void foo(const U& u);
Vector<int> v;
foo(v);
Then inside foo we would need to go through some hoops to find out that T is int. foo only knows about U which is Vector<int>. If Vector<int> has a value_type alias then it is much simpler to access that:
template <typename U>
void foo(const U& u) {
using value_type = typename U::value_type;
//...
}
Now foo does not need to care that U::value_type actually was the T parameter to Vector and is also fine with a type that is not an instantiation of a template:
struct OtherVector {
using value_type = int;
};
OtherVector ov;
foo(ov);
No member alias: "some hoops"
Using a member alias is the simple way. For the sake of completeness I want to show the complicated way that lets foo infer T from Vector<T>. You'll have to bear with me...
First, note that function templates cannot be partially specialized. Thats why I introduce a level of indirection:
template <typename U>
struct foo_impl {
void operator()(const U& u) {
std::cout << "Hello \n";
}
};
template <typename U>
void foo(const U& u) { foo_impl<U>{}(u); }
Caller will call foo and in the background we can mess around with foo_impl.
For example we can add a partial specilization for Vector<T> where we can directly have access to T:
template <typename T>
struct foo_impl< Vector<T> > {
void operator()(const Vector<T>& v) {
std::cout << "Hello Vector<T>\n";
if constexpr (std::is_same_v<int,T>) {
std::cout << "T == int\n";
}
}
};
Live Example.
However, consider what that means for foo. The foo above is fine with any type that has a value_type alias. Now we have a rather boring general definition (prints "Hello") and a specialization for Vector<T> (that prints more when T==int). Thats quite a restriction if we want foo to also work with a
template <typename T>
struct Bar {};
What can we do? We can provide a more generic specialization that matches also instantiations of Bar:
template <template<class> class A, class T>
struct foo_impl< A<T> > {
void operator()(const A<T>& v) {
std::cout << "Hello A<T>\n";
if constexpr (std::is_same_v<int,T>) {
std::cout << "T == int\n"; // We know what T is when a Vector<T> is passed !!
}
}
};
Live Example
It is using a template tempalte parameter to match any instantiation of a template with a single template parameter.
Are we fine now? Unfortunately no, because suppose we want to get a "value type" from this:
template <typename A,typename B>
struct Moo {};
Suppose by convention the first parameter, A, is the value type we are looking for. Then we need to add an even more generic specialization:
template <template<class...> class A, typename T,typename... Others>
struct foo_impl< A<T,Others...> > {
void operator()(const A<T,Others...>& v) {
std::cout << "Hello A<T>\n";
if constexpr (std::is_same_v<int,T>) {
std::cout << "T == int\n";
}
}
};
Live Example
This will match an instantiation of a template with any number of type parameters and will detect the first template parameter to be T.
Are we fine yet? Unfortunately: Not at all.
The above does not match a
template <typename A, int x>
struct Omg {};
Because it has a non-type template parameter. We could also fix that, but lets stop here. Requiring that a value type is always the first parameter is too restrictive anyhow.
TL;DR
What we actually want is to make foo work with any type that has an associated "value type". And the simple way to do that is that any type that should be passed to foo to supply a member alias called value_type.

function implementation with enable_if outside of class definition

So basically, I have a very basic generic class for now, currently testing the type_traits header. I am currently trying to make a function to work with certain types i.e arithmetic ones for now.
#include <type_traits>
template <typename T> class Test {
public:
template <typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value>::type print();
};
The function works perfectly and for arithmetic types only.
But I like to keep my classes tidy and only have them have prototypes, while the function implementations are outside of the class.
With standard templates i.e
void test();
template <typename T> void Test<T>::test() {}
It is simple and I know how to, but I have no clue how to declare the implementation outside of the class with "std::enable_if" and every attempt I have made, during compilation says that that the prototype does not match any in the class.
I have managed to find a similar question here but the class there is standard and not generic.
PS. I am using MinGW-w64 with -std=c++17
You need one set of template parameters for the class template and one separate set of template parameters for the member function template. You need to repeat the entire complicated return type, since it's part of the function template signature. And note you cannot repeat the default argument =T, or the compiler will think you're trying to define it twice (without checking whether or not the new definition is identical).
template <typename T> template <typename U>
typename std::enable_if<std::is_arithmetic<U>::value>::type
Test<T>::print()
{
// Implementation here.
}
By the way, you're using the "long way" of writing the type, as was needed in C++11. But C++14 introduced a std::enable_if_t shortcut, and C++17 introduced a std::is_arithmetic_v shortcut. So if you're using C++17, you can also write the type
typename std::enable_if<std::is_arithmetic<U>::value>::type
as just
std::enable_if_t<std::is_arithmetic_v<U>>
If you put the enable_if in the default template parameter, which is imo nicer anyway, the out-of-class definition becomes a bit easier:
template<typename T>
struct Test
{
template <typename S = T
, typename = typename std::enable_if<std::is_arithmetic<S>::value>::type >
void print();
};
template<typename T>
template<typename S, typename>
void Test<T>::print()
{
//some code
}
You can try with
template <typename T>
template <typename U>
std::enable_if_t<std::is_arithmetic<U>::value> Test<T>::print()
{ /* do something */ }
The following is a full working example
#include <iostream>
#include <type_traits>
template <typename T> class Test
{
public:
template <typename U = T>
std::enable_if_t<std::is_arithmetic<U>::value> print();
};
template <typename T>
template <typename U>
std::enable_if_t<std::is_arithmetic<U>::value> Test<T>::print()
{ std::cout << "test!" << std::endl; }
int main ()
{
Test<int> ti;
Test<void> tv;
ti.print(); // compile
//tv.print(); // compilation error
}
Off Topic 1
Observe that your solution can be hijacked in this way
Test<void>{}.print<int>();
To avoid this problem you could impose that T is equal to U,
template <typename T> class Test
{
public:
template <typename U = T>
std::enable_if_t< std::is_arithmetic<U>::value
&& std::is_same<T, U>::value> print()
{ }
};
Off Topic 2
As you can see, you have to repeat the SFINAE part (std::enable_if_t, std::is_arithmetic and std::is_same).
Taking in count that you have to repeat the implementation in an header, I don't think (IMHO) that to write the implementation of template classes outside the body of the class is a great idea.
Since you haven't posted what you attempted I can't tell you where you went wrong. But this is how you would implement the member function outside the class definition (although it still needs to be implemented in the header, so I don't think this is worth the trouble)
template <typename T> class Test {
public:
template <typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value>::type print();
};
template <typename T> // class template parameter
template <typename U> // function template parameter
inline typename std::enable_if<std::is_arithmetic<U>::value>::type Test<T>::print()
{
}
Live demo
template<typename T>
struct test
{
template<typename U = T>
typename std::enable_if<std::is_arithmetic<U>::value>::type print();
};
template<typename T> template<typename U>
typename std::enable_if<std::is_arithmetic<U>::value>::type test<T>::print()
{
}
void foo()
{
test<int> t;
t.print();
test<void*> u;
u.print();
}
If you need an extra template parameter U, as the other answers explained the right syntax is
template<typename T>
struct test
{
template<typename U>
... a_method(...);
};
template<typename T>
template<typename U>
... test<T>::a_method(...)
{
...
}
However in your peculiar case, if you only need to check some properties of the T type this is really an extra complication. Introduction of the U type is "artificial" and is only here because of the SFINAE
IMHO, it is much more elegant and simpler to use if constexpr
#include <iostream>
#include <type_traits>
template <typename T>
class Test
{
public:
void print();
};
template <typename T>
void Test<T>::print()
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "\nOk T is arithmetic";
// ... your implementation here ...
}
else
{
// throw an exception or do what ever you want,
// here a compile-time error
static_assert(!std::is_same_v<T, T>, "not implemented yet...");
}
}
main()
{
Test<int> t;
t.print();
Test<void> t2;
// t2.print(); <- will generate a compile time error
}

C++ SFINAE operator/function result type check

I am writing a template-based class for Polynomials. (Evaluation, some operations between Polynomials, differentiation, ...), like this:
template <typename _ty> class Polynomial{...
For the to_string function (and the std::ostream-left-shift-override), I had to check, if _ty supports the <-operator (i.e. yes for real numbers, no for complex numbers), so that the string could be formatted nicely. For this I use this code:
#include <type_traits>
template <class _op, class... _ty, typename = decltype(std::declval<_op>()(std::declval<_ty>()...))>
std::true_type impl_test(const _op&, const _ty&...) { return std::true_type(); }
std::false_type impl_test(...) { return std::false_type(); }
template <class> struct supports;
template <class _op, class... _ty> struct supports<_op(_ty...)> : decltype(impl_test(std::declval<_op>(), std::declval<_ty>()...)){};
#define is_ineq_supported(type) supports<std::less<>(type, type)>()
In short, is_ineq_supported(type) returns an std::true_type if there is a valid overload for the <-operator, and a false_type if not. The corresponding functions can then be called with a true_type or false_type as the distinguishing argument, like this:
template <typename _ty> void do_stuff(_ty arg, const std::true_type&) {
// do stuff with the '<'-operator
}
template <typename _ty> void do_stuff(_ty arg, const std::false_type&) {
// do stuff without the '<'-operator
}
template <typename _ty> void do_stuff(_ty arg) {
do_stuff(arg, is_ineq_supported(_ty));
}
I also have a Vector class, that overloads the binary *-operator with the dot product, so it returns a double. But for a polynomial, it only makes sense, to have coefficients and arguments, which return the same type when multiplied with one another.
My problem is the following: I'd like to have a way of checking, if the given operation returns a specified type. (maybe a similar macro?) I think, the simplest would be something that returns a true_type if the result type matches the argument type and a false_type otherwise. Of course, more general solutions are even better.
In case, the IDE and Compiler matter: I'm using Visual Studio 2015 with default settings.
Here is a generic, cross-platform solution using fit::is_callable, which will be eventually added to Boost. Also keep your eye out for std::is_callable in C++17.
No macros necessary:
#include <type_traits>
#include <iostream>
#include <fit/is_callable.hpp>
// std::less doesn't SFINAE, so we make our own test
struct less_test {
template<typename L, typename R>
auto operator()(L l, R r) -> decltype(l < r);
};
template<typename T>
using is_less_than_comparable = fit::is_callable<less_test, T, T>;
// operator< version (replace with your implementation)
template <typename T> constexpr auto
do_stuff(T arg, const std::true_type&) {
return std::integral_constant<int, 0>{};
}
// other version (replace with your implementation)
template <typename T> constexpr auto
do_stuff(T arg, const std::false_type&) {
return std::integral_constant<int, 1>{};
}
template <typename T> constexpr auto
do_stuff(T arg) {
return do_stuff(arg, is_less_than_comparable<T>{});
}
struct foo {};
int main() {
//is not less-than comparable
static_assert(do_stuff(foo{}) == 1, "");
//is less-than comparable
static_assert(do_stuff(0) == 0, "");
}
You should probably reconsider your member method detectors and use the most C++11-ish void_t based solution.
That said, if I had to update your solution, probably the following code is a viable approach:
template <class _op, class _ret, class... _ty>
typename std::enable_if<std::is_same<decltype(std::declval<_op>()(std::declval<_ty>()...)), _ret>::value, std::true_type>::type
impl_test(const _op&, const _ty&...) {
return std::true_type();
}
This will work as it follows:
struct S { int operator()() { return 42; } };
int main() {
assert((impl_test<S, int>(S{})));
// this will give you an error at compile time
// assert((impl_test<S, double>(S{})));
}

What does it mean when one says something is SFINAE-friendly?

I can't clearly get the grasp of what it means when one mentions that a particular function, struct or ... is SFINAE-friendly.
Would someone please explain it?
When it allows substitution failure without hard error (as static_assert).
for example
template <typename T>
void call_f(const T& t)
{
t.f();
}
The function is declared for all T, even those which don't have f, so you cannot do SFINAE on call_f<WithoutF> as the method does exist. (Demo of non compiling code).
With following change:
template <typename T>
auto call_f(const T& t) ->decltype(t.f(), void())
{
t.f();
}
The method exists only for valid T.
so you can use SFINAE as
template<typename T>
auto call_f_if_available_impl(const T& t, int) -> decltype(call_f(t))
{
call_f(t);
}
template<typename T>
auto call_f_if_available_impl(const T& t, ...)
{
// Do nothing;
}
template<typename T>
auto call_f_if_available(const T& t)
{
call_f_if_available_impl(t, 0);
}
Note the int = 0 and ... is to order the overload.
Demo
--
An other case is when the template add special parameter to apply SFINAE for specialization:
template <typename T, typename Enabler = void> struct S;
And then
// Specialization only available for T which respect the traits.
template <typename T>
struct S<T, std::enable_if_t<my_type_trait<T>::value>>
{
};
An entity is termed SFINAE-friendly if it can be used in the context of SFINAE without producing a hard error upon substitution failure. I assume you already know what SFINAE is, as that is a whole other question in itself.
In the context of C++ standardization, the term SFINAE-friendly has so far been applied to std::result_of and std::common_type. Take the following example:
template <typename T>
void foo(T x, typename std::common_type<T, int>::type y) {}
void foo(std::string x, std::string y) {}
int main()
{
foo(std::string("hello"), std::string("world"));
}
Without SFINAE-friendly common_type, this would fail to compile, because std::common_type<std::string, int>::type would produce a hard error during template argument substitution. With the introduction of SFINAE-friendly common_type (N3843) this example becomes well-formed, because std::common_type<std::string, int>::type produces a substitution failure so that overload is excluded from the viable set.
Here's a similar example with result_of:
template <typename T>
auto bar(T f) -> typename std::result_of<T()>::type { return f(); }
void bar(int n) {}
int main()
{
bar(42);
}
Without SFINAE-friendly result_of, this would fail to compile, because std::result_of<int()>::type would produce a hard error during template argument substitution. With the introduction of SFINAE-friendly result_of (N3462) this example becomes well-formed, because std::result_of<int()>::type produces a substitution failure so that overload is excluded from the viable set.

Templated class specialization where template argument is a template

I wondering if something similar to this is possible. Basically, I have a templated class that occasionally takes objects of templated classes. I would like to specialize it (or just a member function)for a specific templated class, but the 'generic' form of that class.
template<typename T, typename S>
class SomeRandomClass
{
//put something here
};
template<typename T>
class MyTemplateClass
{
void DoSomething(T & t) {
//...something
}
};
template<>
void MyTemplateClass< SomeRandomClass<???> >::DoSomething(SomeRandomClass<???> & t)
{
//something specialized happens here
}
Replacing the question marks with appropriate types (double, etc) works, but I would like it to remain generic. I don't know what to put there, as any types wouldn't have been defined. I've looked around, and learned about template template parameters, and tried various combinations to no avail. Thanks for the help!
It's possible to specialize the class like this
template <>
template <typename T,typename S>
class MyTemplateClass <SomeRandomClass<T,S> >
{
void DoSomething(SomeRandomClass<T,S>& t) { /* something */ }
};
It's not possible to specialize just the member method, because the specialization is on the class as a whole, and you have to define a new class. You can, however, do
template <>
template <typename T,typename S>
class MyTemplateClass <SomeRandomClass<T,S> >
{
void DoSomething(SomeRandomClass<T,S>& t);
};
template <>
template <typename T,typename S>
void MyTemplateClass<SomeRandomClass<T,S> >::DoSomething(SomeRandomClass<T,S>& t)
{
// something
}
to split up the declaration and definition.
I'm not completely sure why #Ryan Calhoun specialized the way he did but here's a more terse example:
// class we want to specialize with later on
template<typename T, typename S>
struct SomeRandomClass
{
int myInt = 0;
};
// non-specialized class
template<typename T>
struct MyTemplateClass
{
void DoSomething(T & t)
{
std::cout << "Not specialized" << std::endl;
}
};
// specialized class
template<typename T, typename S>
struct MyTemplateClass< SomeRandomClass<T, S> >
{
void DoSomething(SomeRandomClass<T,S> & t)
{
std::cout << "Specialized" << std::endl;
}
};
You can see that you don't need the redundant syntax used in the accepted answer:
template<>
template<typename T, typename S>
Working Demo
Alternative
You can use type_traits and tag-dispatch within your non-specialized class to specialize just the function.
Let's first make a concept for is_random_class:
// concept to test for whether some type is SomeRandomClass<T,S>
template<typename T>
struct is_random_class : std::false_type{};
template<typename T, typename S>
struct is_random_class<SomeRandomClass<T,S>> : std::true_type{};
And then let's declare our MyTemplateClass again, but this time not templated (because we're not specializing) so we'll call it MyNonTemplatedClass:
class MyNonTemplatedClass
{
public:
template<typename T>
void DoSomething(T & t)
{
DoSomethingHelper(t, typename is_random_class<T>::type());
}
// ...
Notice how DoSomething is now templated, and it's actually calling a helper instead of implementing the logic itself?
Let's break down the line:
DoSomethingHelper(t, typename is_random_class<T>::type());
t is as-before; we're passing along the argument of type T&
typename is_random_class<T>::type()
is_random_class<T> is our concept, and since it derives from std::true_type or std::false_type it will have a ::type defined within the class (Google for "type traits")
::type() 'instantiates' the type specified by is_random_class<T>::type. I say it in quotation marks because we're really going to throw that away as we see later
typename is required because the compiler doesn't know that is_random_clas<T>::type actually names a type.
Now we're ready to look at the rest of MyNonTemplatedClass:
private:
//use tag dispatch. If the compiler is smart it won't actually try to instantiate the second param
template<typename T>
void DoSomethingHelper(T&t, std::true_type)
{
std::cout << "Called DoSomething with SomeRandomClass whose myInt member has value " << t.myInt << std::endl;
}
template<typename T>
void DoSomethingHelper(T&t, std::false_type)
{
std::cout << "Called DoSomething with a type that is not SomeRandomClass\n";
}
};
Full Working Demo v2 Here
Notice that our helper functions are named the same, but overloaded on the second parameter's type. We don't give a name to the parameter because we don't need it, and hopefully the compiler will optimize it away while still calling the proper function.
Our concept forces DoSomethingHelper(T&t, std::true_type) only if T is of type SomeRandomClass, and calls the other for any other type.
The benefit of tag dispatch
The main benefit of tag dispatch here is that you don't need to specialize your entire class if you only mean to specialize a single function within that class.
The tag dispatching will happen at compile time, which you wouldn't get if you tried to perform branching on the concept solely within the DoSomething function.
C++17
In C++17, this problem becomes embarrassingly easy using variable templates (C++14) and if constexpr (C++17).
We use our type_trait to create a variable template that will give us a bool value of true if the provided type T is of type SomeRandomClass, and false otherwise:
template<class T>
constexpr bool is_random_class_v = is_random_class<T>::value;
Then, we use it in a if constexpr expression that only compiles the appropriate branch (and discards the other at compile-time, so the check is at compile-time, not run-time):
struct MyNonTemplatedClass
{
template<class T>
void DoSomething(T& t)
{
if constexpr(is_random_class_v<T>)
std::cout << "Called DoSomething with SomeRandomClass whose myInt member has value " << t.myInt << std::endl;
else
std::cout << "Called DoSomething with a type that is not SomeRandomClass\n";
}
};
type-traits were a way to simulate this without needing a class specialization.
Note that is_random_class here is a stand-in for an arbitrary constraint. In general, if you're only checking for a single nontemplated type, prefer a normal overload because it's more efficient on the compiler.
Demo
C++20
In C++20, we can take this a step further and use a constraint instead of if constexpr by using a requires clause on our templated member function. The downside is that we again move back to two functions; one that matches the constraint, and another that doesn't:
struct MyNonTemplatedClass
{
template<class T> requires is_random_class_v<T>
void DoSomething(T& t)
{
std::cout << "Called DoSomething with SomeRandomClass whose myInt member has value " << t.myInt << std::endl;
}
template<class T> requires !is_random_class_v<T>
void DoSomething(T&)
{
std::cout << "Called DoSomething with a type that is not SomeRandomClass\n";
}
};
Demo
Also in C++ 20, we could explicitly encode a concept and use abbreviated template syntax:
template<class T>
concept IsRandomClass = is_random_class_v<T>;
template<class T>
concept IsNotRandomClass = !is_random_class_v<T>;
// ...
template<IsRandomClass T>
void DoSomething(T& t)
{ /*...*/}
template<IsNotRandomClass T>
void DoSomething(T&)
{ /*...*/}
Demo
All you need to do is just template on what you want to keep generic. Taking what you started with:
template<typename T, typename S>
void MyTemplateClass< SomeRandomClass<T,S> >::DoSomething(SomeRandomClass<T,S> & t)
{
//something specialized happens here
}
EDIT:
Alternatively, if you only want to keep part of the SomeRandomClass generic, you could:
template<typename T>
void MyTemplateClass< SomeRandomClass<T,int> >::DoSomething(SomeRandomClass<T,int> & t)
{
//something specialized happens here
}
Edit: this is a correct answer to a different question.
Using the typename T twice confuses the issue a little, because they are compiled separately and are not connected in any way.
You can overload the method to take a templated parameter:
template <typename T>
class MyTemplateClass
{
void DoSomething(T& t) { }
template <typename U,typename V>
void DoSomething(SomeRandomClass<<U,V>& r) { }
};
This maps U and V in the new method to T' and S' in SomeRandomClass. In this setup, either U or V could be the same type as T, but they don't have to be. Depending on your compiler, you ought to be able to do
MyTemplateClass<string> mine;
SomeRandomClass<int,double> random;
// note: nevermind the non-const ref on the string literal here...
mine.DoSomething("hello world");
mine.DoSomething(random);
and the templated call will be selected as the matching overload without having to respecify the types explicitly.
Edit:
To do with with template specialization makes no difference to the overload of DoSomething. If you specialize the class as follows
template <>
class SomeRandomClass <int,double>
{
// something here...
};
then the overload above will eat up this specialized implementation gladly. Just be sure the interfaces of the specialized template and the default template match.
If what you're wanting is to specialize DoSomething to take a specific pair of types for SomeRandomClass then you've already lost generality...that's what specialization is.
If you want to use provide a template struct as a template argument (with intent to use it inside) without specializing it:
Here is an example, that appends a type to a tuple given a template sfinae struct as a template argument:
template<typename Tuple, typename T, template<typename> class /*SFINAEPredicate*/>
struct append_if;
template<typename T, template<typename> class SFINAEPredicate, typename ... Types>
struct append_if<std::tuple<Types...>, T, SFINAEPredicate>
{
using type = typename std::conditional<SFINAEPredicate<T>::value,
std::tuple<Types..., T>, std::tuple<Types...>>::type;
};
// usage
using tuple_with_int = append_if<std::tuple<>, int, std::is_fundamental>;
This can be used since C++11.