I have recently found out that the member functions of class templates aren't instantiated until they're used, which is extremely annoying because it makes certain SFINAE constructs not work. I would like to know how to make sure that a member function of a class template specialization is always instantiated as soon as the class is instantiated -- but only using statements inside the class template definition, so that if the member function could not be instantiated, SFINAE kicks in and the compiler falls back on the general class template.
The code I was planning to use looks like this:
template <typename T, typename U>
class test_type {
// general template; dummy parameter U is to allow specialization.
static const bool value = false;
}
template <typename T>
class test_type<T, T> {
// template specialization
void f(T t) {
// do all sorts of type-specific operations with t
// (e.g., calling member functions)
// if T is not suitable for these operations, I want
// a substitution error to be generated so that the
// general template is used instead
}
static const bool value = true;
}
template <typename T>
using satisfies = test_type<T, T>::value;
The body of the member functions in a class will not be a part of SFINAE. The template gets instantiated depending on the signature, and once the class has been instantiated a faulty member function will lead to a hard error.
What you need to do is build a type_trait that you can place in the template specialization. Here's a toy example that checks for two different member functions.
#include <iostream>
#include <type_traits>
template <typename T>
using check_for_method = std::void_t<decltype(std::declval<T>().run()), decltype(std::declval<T>().go())>;
template <typename T, typename U = void>
class test_type {
public:
static const bool value = false;
};
template <typename T>
class test_type<T, check_for_method<T>> {
public:
void f(T t) {
t.run();
t.go();
}
static const bool value = true;
};
struct Foo {
void run() {
std::cout << "Running\n";
}
};
struct Bar : Foo {
void go() {
std::cout << "Going\n";
}
};
int main() {
test_type<Foo> t1;
test_type<int> t2;
test_type<Bar> t3;
std::cout << t1.value << " " << t2.value << " " << t3.value << std::endl;
t3.f(Bar{});
}
Related
I was under the assumption that decltype(auto) is an incompatible construct when used to try and SFINAE off the return type. So, when you would have otherwise gotten a substitution error you get a hard error
But why does the following program work? https://wandbox.org/permlink/xyvxYsakTD1tM3yl
#include <iostream>
#include <type_traits>
using std::cout;
using std::endl;
template <typename T>
class Identity {
public:
using type = T;
};
template <typename T>
decltype(auto) construct(T&&) {
return T{};
}
template <typename T, typename = std::void_t<>>
class Foo {
public:
static void foo() {
cout << "Nonspecialized foo called" << endl;
}
};
template <typename T>
class Foo<T,
std::void_t<typename decltype(construct(T{}))::type>> {
public:
static void foo() {
cout << "Specialized foo called" << endl;
}
};
int main() {
Foo<Identity<int>>::foo();
Foo<int>::foo();
}
Shouldn't we get a hard error when Foo is instantiated with int? Given that int has no member alias named type?
I was under the assumption that decltype(auto) is an incompatible construct when used to try and SFINAE off the return type.
It is generally incompatible because it forces instantiation of the body of the function. If a substitution failure occurs in the body, then that's going to be a hard compile error - SFINAE doesn't apply here.
However, in this example, the only way that you would get a substitution failure in the body is if T weren't default constructible. But you call construct(T{}), which already requires T to be default constructible - so the failure would happen first or never.
Instead, the substitution failure that happens is in the immediate context of substituting into typename decltype(construct(T{}))::type. Trying to get ::type off of an int happens while we're in the immediate context of instantiating the template arguments to Foo, so SFINAE still applies.
An example demonstrating where decltype(auto) breaks SFINAE is if we had instead implemented this as:
template <typename T>
decltype(auto) construct() {
return T{};
}
template <typename T, typename = std::void_t<>>
class Foo {
public:
static void foo() {
cout << "Nonspecialized foo called" << endl;
}
};
template <typename T>
class Foo<T,
std::void_t<typename decltype(construct<T>())::type>> {
public:
static void foo() {
cout << "Specialized foo called" << endl;
}
};
And then tried to instantiate:
struct X {
X(int);
};
Foo<X>::foo(); // hard error, failure is in the body of construct()
Recently while answering a question here if-else depends on whether T is a complete type I realized that the following does not compile
#include <iostream>
#include <type_traits>
using namespace std;
class Incomplete;
class Complete {};
template <typename IncompleteType>
struct DetermineCompleteHelper : public IncompleteType {};
template <typename IncompleteType, typename = std::enable_if_t<true>>
struct DetermineComplete {
static constexpr const bool value = false;
};
template <typename IncompleteType>
struct DetermineComplete<IncompleteType, std::enable_if_t<std::is_same<
decltype(DetermineCompleteHelper<IncompleteType>{}),
decltype(DetermineCompleteHelper<IncompleteType>{})>::value>> {
static constexpr const bool value = true;
};
int main() {
cout << DetermineComplete<Complete>::value << endl;
cout << DetermineComplete<Incomplete>::value << endl;
return 0;
}
But changing the partial template specialization to
template <typename IncompleteType>
struct DetermineComplete<IncompleteType, std::enable_if_t<std::is_same<
std::integer_sequence<int, sizeof(IncompleteType)>,
std::integer_sequence<int, sizeof(IncompleteType)>>::value>> {
static constexpr const bool value = true;
};
makes the code compile without errors, why this irregularity? Shouldn't the first expression be treated as an error within the context of the partial specialization and thus have SFINAE kick in and make the default definition of the class the one that is instantiated?
The error occurs when attempting to instantiate the definition of DetermineCompleteHelper<IncompleteType> (in particular, it attempts to derive from a base class that is incomplete). That's outside the immediate context.
I would like to write a template function which behaves one way if the passed type is derived from any template instantiation of another class, and another way if not.
I think the code below captures what I would like to do. Unfortunately Caller prints "generic" for both double and Derived.
#include <iostream>
template <typename T>
struct Base
{
};
struct Derived
:
public Base<int>
{
};
template <typename T>
void Foo(const T&)
{
std::cout << "generic" << std::endl;
}
template <typename T>
void Foo(const Base<T>&)
{
std::cout << "derives from Base<T>" << std::endl;
}
template <typename T>
void Caller(const T& t)
{
Foo(t);
}
int main()
{
double x;
Caller(x);
Derived d;
Caller(d);
return 0;
}
(Note that Caller doesn't know which instantiation of Base that its parameter might derive from.)
It's calling the const T& overload because its a better match than const base<T>&. The reason is because calling the first requires no conversions and the second requires a derived-to-base conversion.
Here's a quick hack that shows you how it can be done (note the introduced base class):
#include <iostream>
#include <type_traits>
struct EvenMoreBase {};
template <typename T>
struct Base : EvenMoreBase
{
};
struct Derived
:
public Base<int>
{
};
template <typename T>
typename std::enable_if<!std::is_base_of<EvenMoreBase, T>::value>::type
Foo(const T&)
{
std::cout << "generic" << std::endl;
}
template <typename T>
void Foo(const Base<T>&)
{
std::cout << "derives from Base<T>" << std::endl;
}
template <typename T>
void Caller(const T& t)
{
Foo(t);
}
int main()
{
double x;
Caller(x);
Derived d;
Caller(d);
return 0;
}
If you're able to use C++11 (or <type_traits> in general), the following is also a possible solution and covers not only types T : Base<T>, i.e. instances of the CRTP, but also T : Base<U> without another base class, as requested in your example.
#include <iostream>
#include <type_traits>
template <typename T>
struct Base
{
typedef T base_value_type;
};
struct Derived : public Base<int>
{
};
template <typename T, typename = T>
struct IsDerived
{
static const bool value = false;
};
template <typename T>
struct IsDerived<T, typename std::enable_if<std::is_base_of<Base<typename T::base_value_type>, T>::value, T>::type>
{
static const bool value = true;
};
template <typename T>
void Caller(const T&)
{
std::cout << IsDerived<T>::value << std::endl;
}
int main()
{
Caller(double()); // false
Caller(Derived()); // true
return 0;
}
Note the typedef T base_value_type - which might be called whatever your like. The idea is that each type T derived from Base<U> can leverage the knowledge of the base's template parameter. It doesn't matter if T == U or not. Trying to substitute the second parameter will fail as soon as you pass in a T that has no typedef T base_value_type and thus no specialization for this particular T will be generated.
EDIT: After processing your comment, and inspired by the thread I posted, I tried to somehow extract some base parameter U when examining some time type T : Base<U>. I don't think this can be done in the way you want, i.e. you pass whatever T and you extract U. However, you can do two things.
Simple Solution: If you have control over how derived classes are implemented, instead of adding a typedef in the base class, simply add a corresponding typedef in the derived class:
template <typename BaseParamType>
class Derived : public Base<BaseParamType>
{
public:
typedef BaseParamType base_param_type;
}
or, if you don't want derived classes to be class templates as well, simply hard code the type right into the type (you already know the type of the base parameter):
class Derived : public Base<int>
{
public:
typedef int base_param_type;
}
More involved solution: What you can do, at least for an expected subset of possible Us, is the following:
template <typename DerivedType,
typename BaseParamType = DerivedType,
bool = std::is_base_of<Base<BaseParamType>, DerivedType>::value>
struct Extract
{
typedef BaseParamType type;
};
template <typename T, typename U>
struct Extract<T, U, false>;
int main()
{
Extract<DerivedCRTP>::type; // CRTP - trivial
Extract<Derived, int>::type; // type == int, Derived is derived from Base<int>
Extract<Derived, double>::type; // compile-time error, undefined template
return 0;
}
This isn't as convenient as passing some instance of a type to a deducing template function and have it magically , but you can at least test if some type T derives from Base<U> and get a compile-time error if it doesn't.
Since the base class has to be a concrete class (not a template), it is not possible to know whether it is a template or a non-template class.
In another words :
struct A1 : public B1
{};
struct A2 : public B2<int>
{};
in both of these cases both base classes are concrete types.
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.
Can you specialize a template method within a template class without specializing the class template parameter?
Please note that the specialization is on the value of the template parameter, not its type.
This seems to compile under Visual Studio 2008 SP1 complier, but not GCC 4.2.4.
#include <iostream>
using namespace std;
template <typename T>
class A
{
private:
template <bool b>
void testme();
template <>
void testme<true>() { cout << "true" << endl; };
template <>
void testme<false>() { cout << "false" << endl; };
public:
void test();
};
template<typename T> struct select {};
template<> struct select<int> { static const bool value = true; };
template<> struct select<double> { static const bool value = false; };
template <class T>
void A<T>::test() { testme<select<T>::value>(); }
int main(int argc, const char* argv[])
{
A<int> aInt;
A<double> aDouble;
aInt.test();
aDouble.test();
return 0;
}
GCC tells me:"error: explicit specialization in non-namespace scope ‘class A’"
If it is not supported in the standard, can anyone tell me why?
It is not supported in the standard (and it is apparently a known bug with Visual Studio that you can do it).
The standard doesn't allow an inner template (member function or class) to be specialized without the outer template being specialized as well. One of the reasons for this is that you can normally just overload the function:
template<typename ty>
class A
{
public:
void foo(bool b);
void foo(int i);
};
Is equivalent to:
template<typename ty>
class A
{
public:
template<typename ty2>
void foo(ty2);
template<>
void foo(bool b);
template<>
void foo(int i);
};
here is how you do it:
template<typename A>
struct SomeTempl {
template<bool C> typename enable_if<C>::type
SomeOtherTempl() {
std::cout << "true!";
}
template<bool C> typename enable_if<!C>::type
SomeOtherTempl() {
std::cout << "false!";
}
};
You can get enable_if from my other answer where i told them how to check for a member function's existance in a class using templates. or you can use boost, but remember to change enable_if to enable_if_c then.
Here is another workaround, also useful when you need to partialy specialize a function (which is not allowed). Create a template functor class (ie. class whose sole purpose is to execute a single member function, usually named operator() ), specialize it and then call from within your template function.
I think I learned this trick from Herb Sutter, but do not remember which book (or article) was that. For your needs it is probably overkill, but nonetheless ...
template <typename T>
struct select;
template <bool B>
struct testme_helper
{
void operator()();
};
template <typename T>
class A
{
private:
template <bool B> void testme()
{
testme_helper<B>()();
}
public:
void test()
{
testme<select<T>::value>();
}
};
template<> void testme_helper<true>::operator()()
{
std::cout << "true" << std::endl;
}
template<> void testme_helper<false>::operator()()
{
std::cout << "false" << std::endl;
}
I've never heard of that being possible; it would make sense to me if it was not supported by all compilers. So here is an idea for a workaround:
Implement a template function outside of your class which takes the same action as the method. Then you can specialize this function, and it call it from the method. Of course, you'll also have to pass in any member variables that it needs (and pointers thereto if you want to modify their values).
You could also create another template class as a subclass, and specialize that one, although I've never done this myself and am not 100% sure it would work. (Please comment to augment this answer if you know whether or not this second approach would work!)