Trying to put partial in different header - c++

I have the following program working fine. But once I get rid of the forward declaration of the primary specialization in bar.h. I got compilation error saying the specialization is not a class template. Why?
bar.h:
template<typename T>
struct Bar{
void bar()
{
std::cout<< "bar" << std::endl;
};
};
//template<typename T> // Looks like I need to forward declare this in order to have it compile and work, why?
//struct IsBar;
template<typename T>
struct IsBar<Bar<T>> {
static const bool value = true;
};
main.cpp:
#include "bar.h"
struct Raw{
};
template<typename T>
struct IsBar{
static const bool value = false;
};
template<typename Obj, bool>
struct function{
static void callbar(Obj obj){
obj.bar();
};
};
template<typename Obj>
struct function<Obj, false>{
static void callbar(Obj obj){
std::cout<< "no bar()" << std::endl;
};
};
int main()
{
typedef Bar<Raw> Obj;
Obj obj;
function<Obj, IsBar<Obj>::value> f;
f.callbar(obj);
return 0;
}

This is because
template<typename T>
struct IsBar<Bar<T>> {
static const bool value = true;
};
is a template specialization of the template IsBar<U> for U=Bar<T>. In order to specialize a template, you must first declare the general un-specialized template.
Moreover, for this to work properly (for example in SFINAE), you want the general version to contain the value=false:
template<typename T>
struct IsBar
: std::integral_constant<bool,false>
{};
template<typename T>
struct IsBar<Bar<T>>
: std::integral_constant<bool,true>
{};
As this functionality is closely related to struct Bar<>, it should be defined within the same header file as struct Bar<>, i.e. bar.h in your case.

Related

Partial template specialization using nested (undefined yet) type of a forward-declared class

There is a way to make a partial template specialization for a forward-declared (incomplete) type (answer).
But after seeing the mentioned question I wondered if it is possible to define a partial specialization for a class template using an incomplete nested (and possibly private) class.
In C++ currently one can't forward-declare a nested class without defining the class:
class undefined;
class undefined::foo; // impossibru
With some hacks I made a working code (for a sake of research): https://godbolt.org/z/9W8nfhx8P
#include <iostream>
template <typename T, typename = T>
struct specialize;
// workaround to get to a nested foo
template <typename T, typename...>
struct mem_foo
{
// error: 'struct undefined::foo' is private within this context
using type = typename T::foo;
};
class undefined;
// class undefined::foo; // impossibru
template <typename MemFoo>
struct specialize<typename mem_foo<undefined, MemFoo>::type, MemFoo>
{
void operator()(const MemFoo &f) const
{
// this will compile however
std::cout << f.name << std::endl;
}
};
#include <string>
class undefined
{
private: // will not compile without friend
struct foo{
std::string name = "John Cena";
};
friend struct mem_foo<undefined, foo>; // ugly
// friend struct specialize<foo>; // this is irrelevant, but would be nicer than mem_foo
public:
static foo get() { return {}; }
};
int main()
{
specialize</*undefined::foo*/decltype(undefined::get())>{}(undefined::get());
return 0;
}
But for a private types an ugly friend is used. friend struct specialize<undefined::foo>; would be more semantically appealing.
Is there another or more elegant solution?
A more elegant/less convoluted solution: https://godbolt.org/z/3vrfPWP5f
#include <iostream>
template <typename T, typename = T>
struct specialize;
template <typename T, typename ...>
struct defer_instantiation
{
using type = T;
};
template <typename T, typename ... R>
using defer_instantiation_t = typename defer_instantiation<T, R...>::type;
class undefined;
// class undefined::foo; // impossibru
template <typename MemFoo>
struct specialize<typename defer_instantiation_t<undefined, MemFoo>::foo, MemFoo>
{
void operator()(const MemFoo &f) const
{
// this will compile however
std::cout << f.name << std::endl;
}
};
#include <string>
class undefined
{
private:
struct foo{
std::string name = "John Cena";
};
friend struct specialize<foo>; // this is irrelevant, but would be nicer than mem_foo
public:
static foo get() { return {}; }
};
int main()
{
specialize</*undefined::foo*/decltype(undefined::get())>{}(undefined::get());
return 0;
}
It allowes to reference yet non-existing member types of a yet incomplete class via a deferred template instantiation.

C++ template inheritance with pointer type

I have a template struct and I want that every pointer type instantiates the const version, to avoid having duplicate templates. This doesn't work, but it work with the char pointer. How can I make it work with any pointer?
#include <iostream>
template<class T>
struct Foo {
static void bar() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
// Doesn't work
// template<class T>
// struct Foo<T*> : Foo<const T*> {};
// Works
template<>
struct Foo<char*> : Foo<const char*> {};
int main() {
Foo<char*>::bar();
}
Since T can also be T const, you are creating infinite inheritance, hence the incomplete type error.
Since const T* matches T* with T being char const for example, you makes the class inheriting from itself be inheriting from Foo<T const*>, which expands to Foo<char const const*> which collapses to Foo<char const*>.
Simply add a constraint and your code works as expected:
// Now works
template<class T> requires (not std::is_const_v<T>)
struct Foo<T*> : Foo<const T*> {};
Live example
You can do it if you extract a base class from Foo:
#include <iostream>
template<class T>
struct FooImpl {
static void bar() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
template<class T>
struct Foo : FooImpl<T> {};
template<class T>
struct Foo<T*> : FooImpl<const T*> {};
int main() {
Foo<char*>::bar();
Foo<const char*>::bar();
}
Unlike #Guillaume answer, this does not require C++20.

Conditional compilation and template

Suppose, I have a code:
template <typename T>
class C {
public:
T f() { return m_result; }
void todo() { m_result = doit<T>(); }
private:
T m_result;
};
If T is void, I want to return void and have no m_result at all.
But, the compiler does not allow instantiate a void type.
One of decision is to create a specialization.
template <> class C<void> { /* ... */ }
But I don't what to support the almost identical code.
How can I don't instantiate m_result?
I can use C++17. Thanks!
You could place the data in a base class, then use if constexpr:
template<class T>
struct C_data{
T m_result;
};
template<>
struct C_data<void>{
};
template<class T>
class C: C_data<T>
{
static constexpr auto is_void = std::is_same_v<T,void>;
public:
auto f(){
if constexpr(is_void)
return this->m_result;
else
return;
}
void todo(){
if constexpr(is_void)
this->m_result = doit<T>();
else
doit<T>();
}
};
But it can be argued that a the specialization of the class C is cleaner since all member of a template class should depend on all the template parameter (otherwise you should split your class in order to avoid code bloat).
So I would prefer to fully specialize C, and make part of the class C that are independent of T, a base class of C:
class C_base{
//any thing that is independent of T;
};
template<class T>
class C: public C_base{
//any thing that depend on T
};
template<>
class C<void>: public C_base{
//any thing that depend on T;
};
You could also specialize member funtion by member function, but I find it less clean.
You will find this last code structure in almost all headers of standard library implementations.
This works for me:
#include <type_traits>
template <typename T> T doit() { return T{}; }
template <typename T> struct result_policy { T m_result; };
template <> struct result_policy<void> { };
template <typename T>
class C : private result_policy<T> {
public:
T f(){
if constexpr (!std::is_void_v<T>)
return result_policy<T>::m_result;
}
void todo() {
if constexpr(!std::is_void_v<T>)
result_policy<T>::m_result = doit<T>();
}
};
int main() {
C<int> ci;
ci.todo();
int i = ci.f();
C<void> cv;
cv.todo();
cv.f();
}
I used if constexpr from C++17 to work with m_result and stored m_result into policy struct only for non-void types due to partial template specialization.
If you can use C++17, then try with if constexpr, std::is_same_v<> and std::conditional_t<>:
#include <type_traits>
// auxiliary variable template for checking against void type in a more readable way
template<typename T>
constexpr bool is_void_v = std::is_same_v<T, void>;
// auxiliary alias template for determining the type of the data member
// in order to prevent the compiler from creating member of type "void"
// so if T = void --> data member type as "int"
template<typename T>
using member_type_t = std::conditional_t<!is_void_v<T>, T, int>;
template <typename T>
class C{
public:
T f(){ return (T)m_result; } // no problem if T = void
void todo() {
if constexpr(!is_void_v<T>)
m_result = doit<T>();
else
doit<T>();
}
private:
member_type_t<T> m_result;
};
Actually, as of C++17 there is already a std::is_void_v<> variable template with type_traits.

Disable functions inside templated class

I'm trying to disable some functions inside a simple template class. The functions that should be removed depend on whether the template argument has certain typedefs.
The example boils down to this:
template<typename T>
struct Foo
{
typename T::Nested foo() { return typename T::Nested(); }
int bar() { return 1; }
};
struct NoNested
{
};
struct WithNested
{
typedef int Nested;
};
int main()
{
Foo<WithNested> fwn;
fwn.foo();
fwn.bar();
Foo<NoNested> fnn;
//fnn.foo();
fnn.bar();
}
However this gives me a error: no type named ‘Nested’ in ‘struct NoNested’ style error on gcc and clang++ (mind you old versions of both).
Is there an easy way to remove foo when the typedef T::Nested does not exit? (Other than template specialization of the Foo<T> class, as in the real code I have this for about 5 functions with different typedefs.. which would result in 2^5 different specialization )
EDIT:
Since there has been some asking for the motivation for wanting to do this:
I'd like to create something like acompile time FSM for use in a DSL.
I'd like to be able to do this
struct StateA;
struct StateB;
struct StateC;
struct StateA
{
typedef StateB AfterNext;
};
struct StateB
{
typedef StateA AfterPrev;
typedef StateC AfterNext;
};
struct StateC
{
typedef StateB AfterPrev;
};
template<typename T>
struct FSM
{
FSM<typename T::AfterNext> next() { return FSM<T::AfterNext>(); };
FSM<typename T::AfterPrev> prev() { return FSM<T::AfterPrev>(); };
};
So that
FSM<StateA>().next().prev().next().next();
compiles, but
FSM<StateA>().next().prev().prev();
fails.
Note that in reality there would be more transition functions than this, the transition functions would actually do something, and the FSM would store some state.
UPDATE:
I've created proper examples using the methods that have been given so far.
The answers vary in complexity, and while visitors method is the one I'd probably end up using (as it is simplest), my solution (the most complicated) is the only one that actually removes the function.
You can use class template specialization. If you have several functions, then you can move each function to a base class, and specialize each base class.
Try making function foo template itself. It will compile only when called, so you will get the error only when you will try calling it with NoNested class.
You could add a nested typedef to every class, such that compilation only fails when the function is instantiated.
struct null_type; //an incomplete type, you could use a more descriptive name for your particular problem
template<typename T>
struct Foo
{
typename T::Nested foo() { return typename T::Nested(); }
int bar() { return 1; }
};
struct NoNested
{
typedef null_type Nested;
};
struct WithNested
{
typedef int Nested;
};
int main()
{
Foo<WithNested> fwn;
fwn.foo();
fwn.bar();
Foo<NoNested> fnn;
//fnn.foo(); //attempt to use incomplete type when used
fnn.bar();
}
It is possible to choose the type T::Nested, if it exists, otherwise void, as follows.
The default choice is void:
template<class T, class = void>
struct NestedReturn
{
typedef void type;
};
A template which always returns void, whatever type you give it:
template<class T>
struct Void
{
typedef void type;
};
A specialisation for types with a Nested nested class by SFINAE. Note that typename Void<typename T::Nested>::type is always void, to match the default second parameter of void in the base template:
template<class T>
struct NestedReturn<T, typename Void<typename T::Nested>::type>
{
typedef typename T::Nested type;
};
And now we use it. Note that foo() is not actually removed when there is no T::Nested, but instantiating it causes an error.
template<typename T>
struct Foo
{
typename NestedReturn<T>::type foo() { return typename T::Nested(); }
int bar() { return 1; }
};
struct NoNested
{
};
struct WithNested
{
typedef int Nested;
};
int main()
{
Foo<WithNested> fwn;
fwn.foo();
fwn.bar();
Foo<NoNested> fnn;
//fnn.foo();
fnn.bar();
}
I suspect that using default function template parameters it would be possible to remove foo() properly using SFINAE, but that's only possible in C++11 (untested guesswork):
template<typename T>
struct Foo
{
template<class N = T::Nested>
N foo() { return N(); }
int bar() { return 1; }
};
Here's how I think I can solve it. It's inspired by user763305's comments.
It requires 2*N specialisations rather than 2^N.
template <typename T>
struct has_nested {
// Variables "yes" and "no" are guaranteed to have different sizes,
// specifically sizeof(yes) == 1 and sizeof(no) == 2.
typedef char yes[1];
typedef char no[2];
template <typename C>
static yes& test(typename C::Nested*);
template <typename>
static no& test(...);
// If the "sizeof" the result of calling test<T>(0) would be equal to the sizeof(yes),
// the first overload worked and T has a nested type named type.
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
template<typename T>
struct FooBase
{
int bar() { return 1; }
};
template<typename T, bool>
struct FooImpl : public FooBase<T>
{
};
template<typename T>
struct FooImpl<T,true> : public FooBase<T>
{
typename T::Nested foo() { return typename T::Nested(); }
};
template<typename T>
struct Foo : public FooImpl<T, has_nested<T>::value >
{
};
struct NoNested
{
};
struct WithNested
{
typedef int Nested;
};
int main()
{
Foo<WithNested> fwn;
fwn.foo();
fwn.bar();
Foo<NoNested> fnn;
//fnn.foo();
fnn.bar();
}

How to do one explicit specialization for multiple types?

Considering a template function like below how is it possible to do explicitly specialize one version of function for multiple types:
template <typename T>
void doSomething(){
//whatever
}
The intention is to have one specialization instead of multiple following ones because //something is the same:
void doSomething<int>(){
//something
}
void doSomething<float>(){
//something
}
void doSomething<double>(){
//something
}
any method to achieve one specialization?
You can't make template function specialization. But you could delegate the implementation in a helper class, that can be used from your function. Some skeleton code:
Implement a template class and specialize it:
template< typename T, bool isArithmetic>
struct Something { void operator()() { ... } };
template< typename T, true>
struct Something { void operator()() { ... do something specialized for arithmetic types; } }
Then use it in the template function:
template< typename T>
void myFunction()
{
Something<T, IsArithmetic<T>::value>()();
}
Where IsArithmetic is a class that provides the information about type T (selector). You can find such type info in boost libraries, for example.
You could just have a kind of doSomethingImpl function.
template<typename T> doSomethingImpl() {
// whatever
}
template<typename T> doSomething() {
// something else
}
template<> doSomething<float>() {
doSomethingImpl<float>();
}
template<> doSomething<int>() {
doSomethingImpl<int>();
}
It's also possible to specialize more generically, using SFINAE and std::is_numeric<T>, for example.
using c++ 2011 (option -std=c++11), this works well:
#include <iostream>
template <typename T>
struct unsignedObject
{
unsignedObject() {
std::cout << "instanciate a unsignedObject\n";
}
};
struct signedObject
{
signedObject() {
std::cout << "instanciate a signedObject\n";
}
};
template <typename T>
struct objectImpl
{
typedef unsignedObject<T> impl; // optional default implementation (the line can be removed)
};
template <> struct objectImpl<unsigned char> { typedef unsignedObject<unsigned char> impl; };
template <> struct objectImpl<unsigned int> { typedef unsignedObject<unsigned int> impl; };
template <> struct objectImpl<unsigned short> { typedef unsignedObject<unsigned short> impl; };
template <> struct objectImpl<double> { typedef signedObject impl; };
template <> struct objectImpl<int> { typedef signedObject impl; };
template <> struct objectImpl<short> { typedef signedObject impl; };
template <> struct objectImpl<char> { typedef signedObject impl; };
template <typename T>
using object = typename objectImpl<T>::impl;
int main(void)
{
object<int> x; // x is a signedObject.
object<double> y; // y is a signedObject.
object<unsigned short> z; // z is a unsignedObject.
return 0;
}